diff options
-rw-r--r-- | configure.ac | 15 | ||||
-rw-r--r-- | ggsn/Makefile.am | 11 | ||||
-rw-r--r-- | ggsn/cmdline.c | 39 | ||||
-rw-r--r-- | ggsn/cmdline.ggo | 3 | ||||
-rw-r--r-- | ggsn/cmdline.h | 8 | ||||
-rw-r--r-- | ggsn/ggsn.c | 35 | ||||
-rw-r--r-- | ggsn/gtp-kernel.c | 240 | ||||
-rw-r--r-- | ggsn/gtp-kernel.h | 51 |
8 files changed, 384 insertions, 18 deletions
diff --git a/configure.ac b/configure.ac index cf6f562..383fa6d 100644 --- a/configure.ac +++ b/configure.ac @@ -45,6 +45,17 @@ AC_SUBST(EXEC_LDADD) # FIXME: Replace `main' with a function in `-links': #AC_CHECK_LIB([inks], [main]) +dnl GTP kernel dependencies +AC_ARG_ENABLE([gtp-kernel], + AS_HELP_STRING([--enable-gtp-kernel], [Build GTP tunneling kernel]), + [enable_gtp_kernel="$enableval"], [enable_gtp_kernel="no"]) + +if test "x$enable_gtp_kernel" = "xyes"; then + PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.0.0]) +fi + +AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_kernel" = "yes"]) + # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT @@ -119,3 +130,7 @@ AC_CONFIG_FILES([Makefile libgtp.pc openggsn.spec]) AC_OUTPUT + +echo " +openggsn Configuration: + GTP kernel support: ${enable_gtp_kernel}" diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am index 91df8f0..c8868c1 100644 --- a/ggsn/Makefile.am +++ b/ggsn/Makefile.am @@ -4,7 +4,16 @@ AM_LDFLAGS = @EXEC_LDFLAGS@ AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) +if ENABLE_GTP_KERNEL +AM_CFLAGS += -DGTP_KERNEL +ggsn_LDADD = @EXEC_LDADD@ -lgtp -lgtpnl -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) +else ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) +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 37ed992..a4c25d8 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", @@ -54,6 +52,7 @@ const char *gengetopt_args_info_help[] = { " -q, --qos=INT Requested quality of service (default=`0x0b921f')", " --logfile=STRING Logfile for errors", " --loglevel=STRING Global log ldevel (default=`error')", + " -g, --gtpnl=STRING GTP kernel support (default=`eth0')", 0 }; @@ -123,6 +122,7 @@ void clear_given (struct gengetopt_args_info *args_info) args_info->qos_given = 0 ; args_info->logfile_given = 0 ; args_info->loglevel_given = 0 ; + args_info->gtpnl_given = 0 ; } static @@ -163,6 +163,8 @@ void clear_args (struct gengetopt_args_info *args_info) args_info->logfile_orig = NULL; args_info->loglevel_arg = gengetopt_strdup ("error"); args_info->loglevel_orig = NULL; + args_info->gtpnl_arg = gengetopt_strdup ("eth0"); + args_info->gtpnl_orig = NULL; } @@ -191,6 +193,7 @@ void init_args_info(struct gengetopt_args_info *args_info) args_info->qos_help = gengetopt_args_info_help[17] ; args_info->logfile_help = gengetopt_args_info_help[18] ; args_info->loglevel_help = gengetopt_args_info_help[19] ; + args_info->gtpnl_help = gengetopt_args_info_help[19] ; } @@ -200,9 +203,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) { @@ -304,6 +304,8 @@ cmdline_parser_release (struct gengetopt_args_info *args_info) free_string_field (&(args_info->logfile_orig)); free_string_field (&(args_info->loglevel_arg)); free_string_field (&(args_info->loglevel_orig)); + free_string_field (&(args_info->gtpnl_arg)); + free_string_field (&(args_info->gtpnl_orig)); @@ -374,6 +376,8 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) write_into_file(outfile, "logfile", args_info->logfile_orig, 0); if (args_info->loglevel_given) write_into_file(outfile, "loglevel", args_info->loglevel_orig, 0); + if (args_info->gtpnl_given) + write_into_file(outfile, "gtpnl", args_info->gtpnl_orig, 0); i = EXIT_SUCCESS; @@ -598,7 +602,7 @@ 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; @@ -648,10 +652,11 @@ cmdline_parser_internal ( { "qos", 1, NULL, 'q' }, { "logfile", 1, NULL, 0 }, { "loglevel", 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. */ @@ -747,6 +752,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. */ @@ -920,7 +937,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 9c4c976..47ff102 100644 --- a/ggsn/cmdline.ggo +++ b/ggsn/cmdline.ggo @@ -33,3 +33,6 @@ option "apn" a "Access point name" string default="internet option "qos" q "Requested quality of service" int default="0x0b921f" no option "logfile" - "Logfile for errors" string no option "loglevel" - "Global log ldevel" string default="error" no + +option "gtpnl" g "GTP kernel support" string default="eth0" no + diff --git a/ggsn/cmdline.h b/ggsn/cmdline.h index a87fa4a..150fb4d 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 */ @@ -95,6 +95,9 @@ struct gengetopt_args_info char * loglevel_arg; /**< @brief Global log ldevel (default='error'). */ char * loglevel_orig; /**< @brief Global log ldevel original value given at command line. */ const char *loglevel_help; /**< @brief Global log ldevel 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. */ @@ -116,6 +119,7 @@ struct gengetopt_args_info unsigned int qos_given ; /**< @brief Whether qos was given. */ unsigned int logfile_given ; /**< @brief Whether logfile was given. */ unsigned int loglevel_given ; /**< @brief Whether loglevel was given. */ + unsigned int gtpnl_given ; /**< @brief Whether gtpnl was given. */ } ; @@ -133,8 +137,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 53c8c01..821c942 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -54,6 +54,7 @@ #include "../gtp/pdp.h" #include "../gtp/gtp.h" #include "cmdline.h" +#include "gtp-kernel.h" int end = 0; int maxfd = 0; /* For select() */ @@ -134,6 +135,13 @@ int delete_context(struct pdp_t *pdp) ippool_freeip(ippool, (struct ippoolm_t *)pdp->peer); else SYS_ERR(DGGSN, LOGL_ERROR, 0, "Peer not defined!"); + + if (gtp_kernel_tunnel_del(pdp)) { + SYS_ERR(DGGSN, LOGL_ERROR, 0, + "Cannot delete tunnel from kernel: %s\n", + strerror(errno)); + } + return 0; } @@ -167,6 +175,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 */ } @@ -247,6 +260,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); } @@ -307,6 +322,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); } @@ -502,10 +519,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 */ DEBUGP(DGGSN, "Creating tun interface\n"); if (tun_new((struct tun_t **)&tun)) { @@ -526,6 +551,8 @@ int main(int argc, char **argv) if (ipup) tun_runscript(tun, ipup); +skip_tun: + /******************************************************************/ /* Main select loop */ /******************************************************************/ @@ -556,7 +583,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(DGGSN, LOGL_ERROR, 0, "TUN read failed (fd)=(%d)", tun->fd); @@ -572,11 +599,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_ */ |