aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-08-12 15:07:02 +0200
committerHarald Welte <laforge@gnumonks.org>2017-08-12 15:09:40 +0200
commit556884c7225f0ddcf9b694abcafea8f7a9fcdb83 (patch)
tree399e147b90091a5043e9e0528b79ef8704d8f7f8
parentc14acd39b7f831156cac3ad4e7609b89c40c33a2 (diff)
Introduce a VTY, factually turning OpenGGSN into an Osmocom program
-rw-r--r--ggsn/Makefile.am10
-rw-r--r--ggsn/cmdline.c1162
-rw-r--r--ggsn/cmdline.h275
-rw-r--r--ggsn/ggsn.c854
-rw-r--r--ggsn/ggsn.h130
-rw-r--r--ggsn/ggsn_vty.c788
6 files changed, 1307 insertions, 1912 deletions
diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am
index c945f0b..fef12d3 100644
--- a/ggsn/Makefile.am
+++ b/ggsn/Makefile.am
@@ -2,17 +2,17 @@ bin_PROGRAMS = ggsn
AM_LDFLAGS = @EXEC_LDFLAGS@
-AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS)
+AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
+
+ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
if ENABLE_GTP_KERNEL
AM_CFLAGS += -DGTP_KERNEL
-ggsn_LDADD = @EXEC_LDADD@ -lgtp -lgtpnl -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS)
-else
-ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS)
+ggsn_LDADD += -lgtpnl
endif
ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a
-ggsn_SOURCES = ggsn.c cmdline.c cmdline.h gtp-kernel.h icmpv6.c icmpv6.h checksum.c checksum.h
+ggsn_SOURCES = ggsn_vty.c ggsn.c ggsn.h gtp-kernel.h icmpv6.c icmpv6.h checksum.c checksum.h
if ENABLE_GTP_KERNEL
ggsn_SOURCES += gtp-kernel.c
diff --git a/ggsn/cmdline.c b/ggsn/cmdline.c
deleted file mode 100644
index 31c0744..0000000
--- a/ggsn/cmdline.c
+++ /dev/null
@@ -1,1162 +0,0 @@
-/*
- File autogenerated by gengetopt version 2.22.6
- generated with the following command:
- gengetopt -i cmdline.ggo --conf-parser
-
- The developers of gengetopt consider the fixed text that goes in all
- gengetopt output files to be in the public domain:
- we make no copyright claims on it.
-*/
-
-/* If we use autoconf. */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifndef FIX_UNUSED
-#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */
-#endif
-
-#include <getopt.h>
-
-#include "cmdline.h"
-
-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[] = {
- " -h, --help Print help and exit",
- " -V, --version Print version and exit",
- " -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/')",
- " -l, --listen=STRING Local interface",
- " -n, --net=STRING Network (default=`192.168.0.0/24')",
- " --ipup=STRING Script to run after link-up",
- " --ipdown=STRING Script to run after link-down",
- " --dynip=STRING Dynamic IP address pool",
- " --statip=STRING Static IP address pool",
- " --pcodns1=STRING PCO DNS Server 1 (default=`0.0.0.0')",
- " --pcodns2=STRING PCO DNS Server 2 (default=`0.0.0.0')",
- " --timelimit=INT Exit after timelimit seconds (default=`0')",
- " -a, --apn=STRING Access point name (default=`internet')",
- " -q, --qos=INT Requested quality of service (default=`0x0b921f')",
- " --logfile=STRING Logfile for errors",
- " --loglevel=STRING Global log ldevel (default=`error')",
- " -g, --gtp-linux GTP linux kernel support (default=off)",
- 0
-};
-
-typedef enum {ARG_NO
- , ARG_FLAG
- , ARG_STRING
- , ARG_INT
-} cmdline_parser_arg_type;
-
-static
-void clear_given (struct gengetopt_args_info *args_info);
-static
-void clear_args (struct gengetopt_args_info *args_info);
-
-static int
-cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info,
- struct cmdline_parser_params *params, const char *additional_error);
-
-struct line_list
-{
- char * string_arg;
- struct line_list * next;
-};
-
-static struct line_list *cmd_line_list = 0;
-static struct line_list *cmd_line_list_tmp = 0;
-
-static void
-free_cmd_list(void)
-{
- /* free the list of a previous call */
- if (cmd_line_list)
- {
- while (cmd_line_list) {
- cmd_line_list_tmp = cmd_line_list;
- cmd_line_list = cmd_line_list->next;
- free (cmd_line_list_tmp->string_arg);
- free (cmd_line_list_tmp);
- }
- }
-}
-
-
-static char *
-gengetopt_strdup (const char *s);
-
-static
-void clear_given (struct gengetopt_args_info *args_info)
-{
- args_info->help_given = 0 ;
- args_info->version_given = 0 ;
- args_info->fg_given = 0 ;
- args_info->debug_given = 0 ;
- args_info->conf_given = 0 ;
- args_info->pidfile_given = 0 ;
- args_info->statedir_given = 0 ;
- args_info->listen_given = 0 ;
- args_info->net_given = 0 ;
- args_info->ipup_given = 0 ;
- args_info->ipdown_given = 0 ;
- args_info->dynip_given = 0 ;
- args_info->statip_given = 0 ;
- args_info->pcodns1_given = 0 ;
- args_info->pcodns2_given = 0 ;
- args_info->timelimit_given = 0 ;
- args_info->apn_given = 0 ;
- args_info->qos_given = 0 ;
- args_info->logfile_given = 0 ;
- args_info->loglevel_given = 0 ;
- args_info->gtp_linux_given = 0 ;
-}
-
-static
-void clear_args (struct gengetopt_args_info *args_info)
-{
- FIX_UNUSED (args_info);
- args_info->fg_flag = 0;
- args_info->debug_flag = 0;
- args_info->conf_arg = gengetopt_strdup ("/etc/ggsn.conf");
- args_info->conf_orig = NULL;
- args_info->pidfile_arg = gengetopt_strdup ("/var/run/ggsn.pid");
- args_info->pidfile_orig = NULL;
- args_info->statedir_arg = gengetopt_strdup ("/var/lib/ggsn/");
- args_info->statedir_orig = NULL;
- args_info->listen_arg = NULL;
- args_info->listen_orig = NULL;
- args_info->net_arg = gengetopt_strdup ("192.168.0.0/24");
- args_info->net_orig = NULL;
- args_info->ipup_arg = NULL;
- args_info->ipup_orig = NULL;
- args_info->ipdown_arg = NULL;
- args_info->ipdown_orig = NULL;
- args_info->dynip_arg = NULL;
- args_info->dynip_orig = NULL;
- args_info->statip_arg = NULL;
- args_info->statip_orig = NULL;
- args_info->pcodns1_arg = gengetopt_strdup ("0.0.0.0");
- args_info->pcodns1_orig = NULL;
- args_info->pcodns2_arg = gengetopt_strdup ("0.0.0.0");
- args_info->pcodns2_orig = NULL;
- args_info->timelimit_arg = 0;
- args_info->timelimit_orig = NULL;
- args_info->apn_arg = gengetopt_strdup ("internet");
- args_info->apn_orig = NULL;
- args_info->qos_arg = 0x0b921f;
- args_info->qos_orig = NULL;
- args_info->logfile_arg = NULL;
- args_info->logfile_orig = NULL;
- args_info->loglevel_arg = gengetopt_strdup ("error");
- args_info->loglevel_orig = NULL;
- args_info->gtp_linux_flag = 0;
-
-}
-
-static
-void init_args_info(struct gengetopt_args_info *args_info)
-{
-
-
- args_info->help_help = gengetopt_args_info_help[0] ;
- args_info->version_help = gengetopt_args_info_help[1] ;
- args_info->fg_help = gengetopt_args_info_help[2] ;
- args_info->debug_help = gengetopt_args_info_help[3] ;
- args_info->conf_help = gengetopt_args_info_help[4] ;
- args_info->pidfile_help = gengetopt_args_info_help[5] ;
- args_info->statedir_help = gengetopt_args_info_help[6] ;
- args_info->listen_help = gengetopt_args_info_help[7] ;
- args_info->net_help = gengetopt_args_info_help[8] ;
- args_info->ipup_help = gengetopt_args_info_help[9] ;
- args_info->ipdown_help = gengetopt_args_info_help[10] ;
- args_info->dynip_help = gengetopt_args_info_help[11] ;
- args_info->statip_help = gengetopt_args_info_help[12] ;
- args_info->pcodns1_help = gengetopt_args_info_help[13] ;
- args_info->pcodns2_help = gengetopt_args_info_help[14] ;
- args_info->timelimit_help = gengetopt_args_info_help[15] ;
- 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->loglevel_help = gengetopt_args_info_help[19] ;
- args_info->gtp_linux_help = gengetopt_args_info_help[20] ;
-
-}
-
-void
-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) {
- cmdline_parser_print_version ();
-
- if (strlen(gengetopt_args_info_purpose) > 0)
- printf("\n%s\n", gengetopt_args_info_purpose);
-
- if (strlen(gengetopt_args_info_usage) > 0)
- printf("\n%s\n", gengetopt_args_info_usage);
-
- printf("\n");
-
- if (strlen(gengetopt_args_info_description) > 0)
- printf("%s\n\n", gengetopt_args_info_description);
-}
-
-void
-cmdline_parser_print_help (void)
-{
- int i = 0;
- print_help_common();
- while (gengetopt_args_info_help[i])
- printf("%s\n", gengetopt_args_info_help[i++]);
-}
-
-void
-cmdline_parser_init (struct gengetopt_args_info *args_info)
-{
- clear_given (args_info);
- clear_args (args_info);
- init_args_info (args_info);
-}
-
-void
-cmdline_parser_params_init(struct cmdline_parser_params *params)
-{
- if (params)
- {
- params->override = 0;
- params->initialize = 1;
- params->check_required = 1;
- params->check_ambiguity = 0;
- params->print_errors = 1;
- }
-}
-
-struct cmdline_parser_params *
-cmdline_parser_params_create(void)
-{
- struct cmdline_parser_params *params =
- (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params));
- cmdline_parser_params_init(params);
- return params;
-}
-
-static void
-free_string_field (char **s)
-{
- if (*s)
- {
- free (*s);
- *s = 0;
- }
-}
-
-
-static void
-cmdline_parser_release (struct gengetopt_args_info *args_info)
-{
-
- free_string_field (&(args_info->conf_arg));
- free_string_field (&(args_info->conf_orig));
- free_string_field (&(args_info->pidfile_arg));
- free_string_field (&(args_info->pidfile_orig));
- free_string_field (&(args_info->statedir_arg));
- free_string_field (&(args_info->statedir_orig));
- free_string_field (&(args_info->listen_arg));
- free_string_field (&(args_info->listen_orig));
- free_string_field (&(args_info->net_arg));
- free_string_field (&(args_info->net_orig));
- free_string_field (&(args_info->ipup_arg));
- free_string_field (&(args_info->ipup_orig));
- free_string_field (&(args_info->ipdown_arg));
- free_string_field (&(args_info->ipdown_orig));
- free_string_field (&(args_info->dynip_arg));
- free_string_field (&(args_info->dynip_orig));
- free_string_field (&(args_info->statip_arg));
- free_string_field (&(args_info->statip_orig));
- free_string_field (&(args_info->pcodns1_arg));
- free_string_field (&(args_info->pcodns1_orig));
- free_string_field (&(args_info->pcodns2_arg));
- free_string_field (&(args_info->pcodns2_orig));
- free_string_field (&(args_info->timelimit_orig));
- free_string_field (&(args_info->apn_arg));
- free_string_field (&(args_info->apn_orig));
- 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->loglevel_arg));
- free_string_field (&(args_info->loglevel_orig));
-
-
-
- clear_given (args_info);
-}
-
-
-static void
-write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[])
-{
- FIX_UNUSED (values);
- if (arg) {
- fprintf(outfile, "%s=\"%s\"\n", opt, arg);
- } else {
- fprintf(outfile, "%s\n", opt);
- }
-}
-
-
-int
-cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
-{
- int i = 0;
-
- if (!outfile)
- {
- fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE);
- return EXIT_FAILURE;
- }
-
- if (args_info->help_given)
- write_into_file(outfile, "help", 0, 0 );
- if (args_info->version_given)
- write_into_file(outfile, "version", 0, 0 );
- if (args_info->fg_given)
- write_into_file(outfile, "fg", 0, 0 );
- if (args_info->debug_given)
- write_into_file(outfile, "debug", 0, 0 );
- if (args_info->conf_given)
- write_into_file(outfile, "conf", args_info->conf_orig, 0);
- if (args_info->pidfile_given)
- write_into_file(outfile, "pidfile", args_info->pidfile_orig, 0);
- if (args_info->statedir_given)
- write_into_file(outfile, "statedir", args_info->statedir_orig, 0);
- if (args_info->listen_given)
- write_into_file(outfile, "listen", args_info->listen_orig, 0);
- if (args_info->net_given)
- write_into_file(outfile, "net", args_info->net_orig, 0);
- if (args_info->ipup_given)
- write_into_file(outfile, "ipup", args_info->ipup_orig, 0);
- if (args_info->ipdown_given)
- write_into_file(outfile, "ipdown", args_info->ipdown_orig, 0);
- if (args_info->dynip_given)
- write_into_file(outfile, "dynip", args_info->dynip_orig, 0);
- if (args_info->statip_given)
- write_into_file(outfile, "statip", args_info->statip_orig, 0);
- if (args_info->pcodns1_given)
- write_into_file(outfile, "pcodns1", args_info->pcodns1_orig, 0);
- if (args_info->pcodns2_given)
- write_into_file(outfile, "pcodns2", args_info->pcodns2_orig, 0);
- if (args_info->timelimit_given)
- write_into_file(outfile, "timelimit", args_info->timelimit_orig, 0);
- if (args_info->apn_given)
- write_into_file(outfile, "apn", args_info->apn_orig, 0);
- if (args_info->qos_given)
- 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->loglevel_given)
- write_into_file(outfile, "loglevel", args_info->loglevel_orig, 0);
- if (args_info->gtp_linux_given)
- write_into_file(outfile, "gtp-linux", 0, 0 );
-
-
- i = EXIT_SUCCESS;
- return i;
-}
-
-int
-cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info)
-{
- FILE *outfile;
- int i = 0;
-
- outfile = fopen(filename, "w");
-
- if (!outfile)
- {
- fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename);
- return EXIT_FAILURE;
- }
-
- i = cmdline_parser_dump(outfile, args_info);
- fclose (outfile);
-
- return i;
-}
-
-void
-cmdline_parser_free (struct gengetopt_args_info *args_info)
-{
- cmdline_parser_release (args_info);
-}
-
-/** @brief replacement of strdup, which is not standard */
-char *
-gengetopt_strdup (const char *s)
-{
- char *result = 0;
- if (!s)
- return result;
-
- result = (char*)malloc(strlen(s) + 1);
- if (result == (char*)0)
- return (char*)0;
- strcpy(result, s);
- return result;
-}
-
-int
-cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info)
-{
- return cmdline_parser2 (argc, argv, args_info, 0, 1, 1);
-}
-
-int
-cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info,
- struct cmdline_parser_params *params)
-{
- int result;
- result = cmdline_parser_internal (argc, argv, args_info, params, 0);
-
- if (result == EXIT_FAILURE)
- {
- cmdline_parser_free (args_info);
- exit (EXIT_FAILURE);
- }
-
- return result;
-}
-
-int
-cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required)
-{
- int result;
- struct cmdline_parser_params params;
-
- params.override = override;
- params.initialize = initialize;
- params.check_required = check_required;
- params.check_ambiguity = 0;
- params.print_errors = 1;
-
- result = cmdline_parser_internal (argc, argv, args_info, &params, 0);
-
- if (result == EXIT_FAILURE)
- {
- cmdline_parser_free (args_info);
- exit (EXIT_FAILURE);
- }
-
- return result;
-}
-
-int
-cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name)
-{
- FIX_UNUSED (args_info);
- FIX_UNUSED (prog_name);
- return EXIT_SUCCESS;
-}
-
-
-static char *package_name = 0;
-
-/**
- * @brief updates an option
- * @param field the generic pointer to the field to update
- * @param orig_field the pointer to the orig field
- * @param field_given the pointer to the number of occurrence of this option
- * @param prev_given the pointer to the number of occurrence already seen
- * @param value the argument for this option (if null no arg was specified)
- * @param possible_values the possible values for this option (if specified)
- * @param default_value the default value (in case the option only accepts fixed values)
- * @param arg_type the type of this option
- * @param check_ambiguity @see cmdline_parser_params.check_ambiguity
- * @param override @see cmdline_parser_params.override
- * @param no_free whether to free a possible previous value
- * @param multiple_option whether this is a multiple option
- * @param long_opt the corresponding long option
- * @param short_opt the corresponding short option (or '-' if none)
- * @param additional_error possible further error specification
- */
-static
-int update_arg(void *field, char **orig_field,
- unsigned int *field_given, unsigned int *prev_given,
- char *value, const char *possible_values[],
- const char *default_value,
- cmdline_parser_arg_type arg_type,
- int check_ambiguity, int override,
- int no_free, int multiple_option,
- const char *long_opt, char short_opt,
- const char *additional_error)
-{
- char *stop_char = 0;
- const char *val = value;
- int found;
- char **string_field;
- FIX_UNUSED (field);
-
- stop_char = 0;
- found = 0;
-
- if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given)))
- {
- if (short_opt != '-')
- fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n",
- package_name, long_opt, short_opt,
- (additional_error ? additional_error : ""));
- else
- fprintf (stderr, "%s: `--%s' option given more than once%s\n",
- package_name, long_opt,
- (additional_error ? additional_error : ""));
- return 1; /* failure */
- }
-
- FIX_UNUSED (default_value);
-
- if (field_given && *field_given && ! override)
- return 0;
- if (prev_given)
- (*prev_given)++;
- if (field_given)
- (*field_given)++;
- if (possible_values)
- val = possible_values[found];
-
- switch(arg_type) {
- case ARG_FLAG:
- *((int *)field) = !*((int *)field);
- break;
- case ARG_INT:
- if (val) *((int *)field) = strtol (val, &stop_char, 0);
- break;
- case ARG_STRING:
- if (val) {
- string_field = (char **)field;
- if (!no_free && *string_field)
- free (*string_field); /* free previous string */
- *string_field = gengetopt_strdup (val);
- }
- break;
- default:
- break;
- };
-
- /* check numeric conversion */
- switch(arg_type) {
- case ARG_INT:
- if (val && !(stop_char && *stop_char == '\0')) {
- fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val);
- return 1; /* failure */
- }
- break;
- default:
- ;
- };
-
- /* store the original value */
- switch(arg_type) {
- case ARG_NO:
- case ARG_FLAG:
- break;
- default:
- if (value && orig_field) {
- if (no_free) {
- *orig_field = value;
- } else {
- if (*orig_field)
- free (*orig_field); /* free previous string */
- *orig_field = gengetopt_strdup (value);
- }
- }
- };
-
- return 0; /* OK */
-}
-
-
-int
-cmdline_parser_internal (
- int argc, char **argv, struct gengetopt_args_info *args_info,
- struct cmdline_parser_params *params, const char *additional_error)
-{
- int c; /* Character of the parsed option. */
-
- int error_occurred = 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)
- cmdline_parser_init (args_info);
-
- cmdline_parser_init (&local_args_info);
-
- optarg = 0;
- optind = 0;
- opterr = params->print_errors;
- optopt = '?';
-
- while (1)
- {
- int option_index = 0;
-
- static struct option long_options[] = {
- { "help", 0, NULL, 'h' },
- { "version", 0, NULL, 'V' },
- { "fg", 0, NULL, 'f' },
- { "debug", 0, NULL, 'd' },
- { "conf", 1, NULL, 'c' },
- { "pidfile", 1, NULL, 0 },
- { "statedir", 1, NULL, 0 },
- { "listen", 1, NULL, 'l' },
- { "net", 1, NULL, 'n' },
- { "ipup", 1, NULL, 0 },
- { "ipdown", 1, NULL, 0 },
- { "dynip", 1, NULL, 0 },
- { "statip", 1, NULL, 0 },
- { "pcodns1", 1, NULL, 0 },
- { "pcodns2", 1, NULL, 0 },
- { "timelimit", 1, NULL, 0 },
- { "apn", 1, NULL, 'a' },
- { "qos", 1, NULL, 'q' },
- { "logfile", 1, NULL, 0 },
- { "loglevel", 1, NULL, 0 },
- { "gtp-linux", 0, NULL, 'g' },
- { 0, 0, 0, 0 }
- };
-
- c = getopt_long (argc, argv, "hVfdc:l:n:a:q:g", long_options, &option_index);
-
- if (c == -1) break; /* Exit from `while (1)' loop. */
-
- switch (c)
- {
- case 'h': /* Print help and exit. */
- cmdline_parser_print_help ();
- cmdline_parser_free (&local_args_info);
- exit (EXIT_SUCCESS);
-
- case 'V': /* Print version and exit. */
- cmdline_parser_print_version ();
- cmdline_parser_free (&local_args_info);
- exit (EXIT_SUCCESS);
-
- case 'f': /* Run in foreground. */
-
-
- if (update_arg((void *)&(args_info->fg_flag), 0, &(args_info->fg_given),
- &(local_args_info.fg_given), optarg, 0, 0, ARG_FLAG,
- check_ambiguity, override, 1, 0, "fg", 'f',
- additional_error))
- goto failure;
-
- break;
- case 'd': /* Run in debug mode. */
-
-
- if (update_arg((void *)&(args_info->debug_flag), 0, &(args_info->debug_given),
- &(local_args_info.debug_given), optarg, 0, 0, ARG_FLAG,
- check_ambiguity, override, 1, 0, "debug", 'd',
- additional_error))
- goto failure;
-
- break;
- case 'c': /* Read configuration file. */
-
-
- if (update_arg( (void *)&(args_info->conf_arg),
- &(args_info->conf_orig), &(args_info->conf_given),
- &(local_args_info.conf_given), optarg, 0, "/etc/ggsn.conf", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "conf", 'c',
- additional_error))
- goto failure;
-
- break;
- case 'l': /* Local interface. */
-
-
- if (update_arg( (void *)&(args_info->listen_arg),
- &(args_info->listen_orig), &(args_info->listen_given),
- &(local_args_info.listen_given), optarg, 0, 0, ARG_STRING,
- check_ambiguity, override, 0, 0,
- "listen", 'l',
- additional_error))
- goto failure;
-
- break;
- case 'n': /* Network. */
-
-
- if (update_arg( (void *)&(args_info->net_arg),
- &(args_info->net_orig), &(args_info->net_given),
- &(local_args_info.net_given), optarg, 0, "192.168.0.0/24", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "net", 'n',
- additional_error))
- goto failure;
-
- break;
- case 'a': /* Access point name. */
-
-
- if (update_arg( (void *)&(args_info->apn_arg),
- &(args_info->apn_orig), &(args_info->apn_given),
- &(local_args_info.apn_given), optarg, 0, "internet", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "apn", 'a',
- additional_error))
- goto failure;
-
- break;
- case 'q': /* Requested quality of service. */
-
-
- if (update_arg( (void *)&(args_info->qos_arg),
- &(args_info->qos_orig), &(args_info->qos_given),
- &(local_args_info.qos_given), optarg, 0, "0x0b921f", ARG_INT,
- check_ambiguity, override, 0, 0,
- "qos", 'q',
- additional_error))
- goto failure;
-
- break;
- case 'g': /* GTP linux kernel support. */
-
-
- if (update_arg((void *)&(args_info->gtp_linux_flag), 0, &(args_info->gtp_linux_given),
- &(local_args_info.gtp_linux_given), optarg, 0, 0, ARG_FLAG,
- check_ambiguity, override, 1, 0, "gtp-linux", 'g',
- additional_error))
- goto failure;
-
- break;
-
- case 0: /* Long option with no short option */
- /* Filename of process id file. */
- if (strcmp (long_options[option_index].name, "pidfile") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->pidfile_arg),
- &(args_info->pidfile_orig), &(args_info->pidfile_given),
- &(local_args_info.pidfile_given), optarg, 0, "/var/run/ggsn.pid", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "pidfile", '-',
- additional_error))
- goto failure;
-
- }
- /* Directory of nonvolatile data. */
- else if (strcmp (long_options[option_index].name, "statedir") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->statedir_arg),
- &(args_info->statedir_orig), &(args_info->statedir_given),
- &(local_args_info.statedir_given), optarg, 0, "/var/lib/ggsn/", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "statedir", '-',
- additional_error))
- goto failure;
-
- }
- /* Script to run after link-up. */
- else if (strcmp (long_options[option_index].name, "ipup") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->ipup_arg),
- &(args_info->ipup_orig), &(args_info->ipup_given),
- &(local_args_info.ipup_given), optarg, 0, 0, ARG_STRING,
- check_ambiguity, override, 0, 0,
- "ipup", '-',
- additional_error))
- goto failure;
-
- }
- /* Script to run after link-down. */
- else if (strcmp (long_options[option_index].name, "ipdown") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->ipdown_arg),
- &(args_info->ipdown_orig), &(args_info->ipdown_given),
- &(local_args_info.ipdown_given), optarg, 0, 0, ARG_STRING,
- check_ambiguity, override, 0, 0,
- "ipdown", '-',
- additional_error))
- goto failure;
-
- }
- /* Dynamic IP address pool. */
- else if (strcmp (long_options[option_index].name, "dynip") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->dynip_arg),
- &(args_info->dynip_orig), &(args_info->dynip_given),
- &(local_args_info.dynip_given), optarg, 0, 0, ARG_STRING,
- check_ambiguity, override, 0, 0,
- "dynip", '-',
- additional_error))
- goto failure;
-
- }
- /* Static IP address pool. */
- else if (strcmp (long_options[option_index].name, "statip") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->statip_arg),
- &(args_info->statip_orig), &(args_info->statip_given),
- &(local_args_info.statip_given), optarg, 0, 0, ARG_STRING,
- check_ambiguity, override, 0, 0,
- "statip", '-',
- additional_error))
- goto failure;
-
- }
- /* PCO DNS Server 1. */
- else if (strcmp (long_options[option_index].name, "pcodns1") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->pcodns1_arg),
- &(args_info->pcodns1_orig), &(args_info->pcodns1_given),
- &(local_args_info.pcodns1_given), optarg, 0, "0.0.0.0", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "pcodns1", '-',
- additional_error))
- goto failure;
-
- }
- /* PCO DNS Server 2. */
- else if (strcmp (long_options[option_index].name, "pcodns2") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->pcodns2_arg),
- &(args_info->pcodns2_orig), &(args_info->pcodns2_given),
- &(local_args_info.pcodns2_given), optarg, 0, "0.0.0.0", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "pcodns2", '-',
- additional_error))
- goto failure;
-
- }
- /* Exit after timelimit seconds. */
- else if (strcmp (long_options[option_index].name, "timelimit") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->timelimit_arg),
- &(args_info->timelimit_orig), &(args_info->timelimit_given),
- &(local_args_info.timelimit_given), optarg, 0, "0", ARG_INT,
- check_ambiguity, override, 0, 0,
- "timelimit", '-',
- additional_error))
- goto failure;
-
- }
- /* Logfile for errors. */
- else if (strcmp (long_options[option_index].name, "logfile") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->logfile_arg),
- &(args_info->logfile_orig), &(args_info->logfile_given),
- &(local_args_info.logfile_given), optarg, 0, 0, ARG_STRING,
- check_ambiguity, override, 0, 0,
- "logfile", '-',
- additional_error))
- goto failure;
-
- }
- /* Global log ldevel. */
- else if (strcmp (long_options[option_index].name, "loglevel") == 0)
- {
-
-
- if (update_arg( (void *)&(args_info->loglevel_arg),
- &(args_info->loglevel_orig), &(args_info->loglevel_given),
- &(local_args_info.loglevel_given), optarg, 0, "error", ARG_STRING,
- check_ambiguity, override, 0, 0,
- "loglevel", '-',
- additional_error))
- goto failure;
-
- }
-
- break;
- case '?': /* Invalid option. */
- /* `getopt_long' already printed an error message. */
- goto failure;
-
- default: /* bug: option not considered. */
- fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : ""));
- abort ();
- } /* switch */
- } /* while */
-
-
-
-
- cmdline_parser_release (&local_args_info);
-
- if ( error_occurred )
- return (EXIT_FAILURE);
-
- return 0;
-
-failure:
-
- cmdline_parser_release (&local_args_info);
- return (EXIT_FAILURE);
-}
-
-#ifndef CONFIG_FILE_LINE_SIZE
-#define CONFIG_FILE_LINE_SIZE 2048
-#endif
-#define ADDITIONAL_ERROR " in configuration file "
-
-#define CONFIG_FILE_LINE_BUFFER_SIZE (CONFIG_FILE_LINE_SIZE+3)
-/* 3 is for "--" and "=" */
-
-static int
-_cmdline_parser_configfile (const char *filename, int *my_argc)
-{
- FILE* file;
- char my_argv[CONFIG_FILE_LINE_BUFFER_SIZE+1];
- char linebuf[CONFIG_FILE_LINE_SIZE];
- int line_num = 0;
- int result = 0, equal;
- char *fopt, *farg;
- char *str_index;
- size_t len, next_token;
- char delimiter;
-
- if ((file = fopen(filename, "r")) == 0)
- {
- fprintf (stderr, "%s: Error opening configuration file '%s'\n",
- CMDLINE_PARSER_PACKAGE, filename);
- return EXIT_FAILURE;
- }
-
- while ((fgets(linebuf, CONFIG_FILE_LINE_SIZE, file)) != 0)
- {
- ++line_num;
- my_argv[0] = '\0';
- len = strlen(linebuf);
- if (len > (CONFIG_FILE_LINE_BUFFER_SIZE-1))
- {
- fprintf (stderr, "%s:%s:%d: Line too long in configuration file\n",
- CMDLINE_PARSER_PACKAGE, filename, line_num);
- result = EXIT_FAILURE;
- break;
- }
-
- /* find first non-whitespace character in the line */
- next_token = strspn (linebuf, " \t\r\n");
- str_index = linebuf + next_token;
-
- if ( str_index[0] == '\0' || str_index[0] == '#')
- continue; /* empty line or comment line is skipped */
-
- fopt = str_index;
-
- /* truncate fopt at the end of the first non-valid character */
- next_token = strcspn (fopt, " \t\r\n=");
-
- if (fopt[next_token] == '\0') /* the line is over */
- {
- farg = 0;
- equal = 0;
- goto noarg;
- }
-
- /* remember if equal sign is present */
- equal = (fopt[next_token] == '=');
- fopt[next_token++] = '\0';
-
- /* advance pointers to the next token after the end of fopt */
- next_token += strspn (fopt + next_token, " \t\r\n");
-
- /* check for the presence of equal sign, and if so, skip it */
- if ( !equal )
- if ((equal = (fopt[next_token] == '=')))
- {
- next_token++;
- next_token += strspn (fopt + next_token, " \t\r\n");
- }
- str_index += next_token;
-
- /* find argument */
- farg = str_index;
- if ( farg[0] == '\"' || farg[0] == '\'' )
- { /* quoted argument */
- str_index = strchr (++farg, str_index[0] ); /* skip opening quote */
- if (! str_index)
- {
- fprintf
- (stderr,
- "%s:%s:%d: unterminated string in configuration file\n",
- CMDLINE_PARSER_PACKAGE, filename, line_num);
- result = EXIT_FAILURE;
- break;
- }
- }
- else
- { /* read up the remaining part up to a delimiter */
- next_token = strcspn (farg, " \t\r\n#\'\"");
- str_index += next_token;
- }
-
- /* truncate farg at the delimiter and store it for further check */
- delimiter = *str_index, *str_index++ = '\0';
-
- /* everything but comment is illegal at the end of line */
- if (delimiter != '\0' && delimiter != '#')
- {
- str_index += strspn(str_index, " \t\r\n");
- if (*str_index != '\0' && *str_index != '#')
- {
- fprintf
- (stderr,
- "%s:%s:%d: malformed string in configuration file\n",
- CMDLINE_PARSER_PACKAGE, filename, line_num);
- result = EXIT_FAILURE;
- break;
- }
- }
-
- noarg:
- if (!strcmp(fopt,"include")) {
- if (farg && *farg) {
- result = _cmdline_parser_configfile(farg, my_argc);
- } else {
- fprintf(stderr, "%s:%s:%d: include requires a filename argument.\n",
- CMDLINE_PARSER_PACKAGE, filename, line_num);
- }
- continue;
- }
- len = strlen(fopt);
- strcat (my_argv, len > 1 ? "--" : "-");
- strcat (my_argv, fopt);
- if (len > 1 && ((farg && *farg) || equal))
- strcat (my_argv, "=");
- if (farg && *farg)
- strcat (my_argv, farg);
- ++(*my_argc);
-
- cmd_line_list_tmp = (struct line_list *) malloc (sizeof (struct line_list));
- cmd_line_list_tmp->next = cmd_line_list;
- cmd_line_list = cmd_line_list_tmp;
- cmd_line_list->string_arg = gengetopt_strdup(my_argv);
- } /* while */
-
- if (file)
- fclose(file);
- return result;
-}
-
-int
-cmdline_parser_configfile (
- const char *filename,
- struct gengetopt_args_info *args_info,
- int override, int initialize, int check_required)
-{
- struct cmdline_parser_params params;
-
- params.override = override;
- params.initialize = initialize;
- params.check_required = check_required;
- params.check_ambiguity = 0;
- params.print_errors = 1;
-
- return cmdline_parser_config_file (filename, args_info, &params);
-}
-
-int
-cmdline_parser_config_file (const char *filename,
- struct gengetopt_args_info *args_info,
- struct cmdline_parser_params *params)
-{
- int i, result;
- int my_argc = 1;
- char **my_argv_arg;
- char *additional_error;
-
- /* store the program name */
- cmd_line_list_tmp = (struct line_list *) malloc (sizeof (struct line_list));
- cmd_line_list_tmp->next = cmd_line_list;
- cmd_line_list = cmd_line_list_tmp;
- cmd_line_list->string_arg = gengetopt_strdup (CMDLINE_PARSER_PACKAGE);
-
- result = _cmdline_parser_configfile(filename, &my_argc);
-
- if (result != EXIT_FAILURE) {
- my_argv_arg = (char **) malloc((my_argc+1) * sizeof(char *));
- cmd_line_list_tmp = cmd_line_list;
-
- for (i = my_argc - 1; i >= 0; --i) {
- my_argv_arg[i] = cmd_line_list_tmp->string_arg;
- cmd_line_list_tmp = cmd_line_list_tmp->next;
- }
-
- my_argv_arg[my_argc] = 0;
-
- additional_error = (char *)malloc(strlen(filename) + strlen(ADDITIONAL_ERROR) + 1);
- strcpy (additional_error, ADDITIONAL_ERROR);
- strcat (additional_error, filename);
- result =
- cmdline_parser_internal (my_argc, my_argv_arg, args_info,
- params,
- additional_error);
-
- free (additional_error);
- free (my_argv_arg);
- }
-
- free_cmd_list();
- if (result == EXIT_FAILURE)
- {
- cmdline_parser_free (args_info);
- exit (EXIT_FAILURE);
- }
-
- return result;
-}
diff --git a/ggsn/cmdline.h b/ggsn/cmdline.h
deleted file mode 100644
index 57f5cae..0000000
--- a/ggsn/cmdline.h
+++ /dev/null
@@ -1,275 +0,0 @@
-/** @file cmdline.h
- * @brief The header file for the command line option parser
- * generated by GNU Gengetopt version 2.22.6
- * http://www.gnu.org/software/gengetopt.
- * DO NOT modify this file, since it can be overwritten
- * @author GNU Gengetopt by Lorenzo Bettini */
-
-#ifndef CMDLINE_H
-#define CMDLINE_H
-
-/* If we use autoconf. */
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdio.h> /* for FILE */
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-#ifndef CMDLINE_PARSER_PACKAGE
-/** @brief the program name (used for printing errors) */
-#define CMDLINE_PARSER_PACKAGE PACKAGE
-#endif
-
-#ifndef CMDLINE_PARSER_PACKAGE_NAME
-/** @brief the complete program name (used for help and version) */
-#ifdef PACKAGE_NAME
-#define CMDLINE_PARSER_PACKAGE_NAME PACKAGE_NAME
-#else
-#define CMDLINE_PARSER_PACKAGE_NAME PACKAGE
-#endif
-#endif
-
-#ifndef CMDLINE_PARSER_VERSION
-/** @brief the program version */
-#define CMDLINE_PARSER_VERSION VERSION
-#endif
-
-/** @brief Where the command line options are stored */
-struct gengetopt_args_info
-{
- const char *help_help; /**< @brief Print help and exit help description. */
- const char *version_help; /**< @brief Print version and exit help description. */
- int fg_flag; /**< @brief Run in foreground (default=off). */
- const char *fg_help; /**< @brief Run in foreground help description. */
- int debug_flag; /**< @brief Run in debug mode (default=off). */
- const char *debug_help; /**< @brief Run in debug mode help description. */
- char * conf_arg; /**< @brief Read configuration file (default='/etc/ggsn.conf'). */
- char * conf_orig; /**< @brief Read configuration file original value given at command line. */
- const char *conf_help; /**< @brief Read configuration file help description. */
- char * pidfile_arg; /**< @brief Filename of process id file (default='/var/run/ggsn.pid'). */
- char * pidfile_orig; /**< @brief Filename of process id file original value given at command line. */
- const char *pidfile_help; /**< @brief Filename of process id file help description. */
- char * statedir_arg; /**< @brief Directory of nonvolatile data (default='/var/lib/ggsn/'). */
- char * statedir_orig; /**< @brief Directory of nonvolatile data original value given at command line. */
- const char *statedir_help; /**< @brief Directory of nonvolatile data help description. */
- char * listen_arg; /**< @brief Local interface. */
- char * listen_orig; /**< @brief Local interface original value given at command line. */
- const char *listen_help; /**< @brief Local interface help description. */
- char * net_arg; /**< @brief Network (default='192.168.0.0/24'). */
- char * net_orig; /**< @brief Network original value given at command line. */
- const char *net_help; /**< @brief Network help description. */
- char * ipup_arg; /**< @brief Script to run after link-up. */
- char * ipup_orig; /**< @brief Script to run after link-up original value given at command line. */
- const char *ipup_help; /**< @brief Script to run after link-up help description. */
- char * ipdown_arg; /**< @brief Script to run after link-down. */
- char * ipdown_orig; /**< @brief Script to run after link-down original value given at command line. */
- const char *ipdown_help; /**< @brief Script to run after link-down help description. */
- char * dynip_arg; /**< @brief Dynamic IP address pool. */
- char * dynip_orig; /**< @brief Dynamic IP address pool original value given at command line. */
- const char *dynip_help; /**< @brief Dynamic IP address pool help description. */
- char * statip_arg; /**< @brief Static IP address pool. */
- char * statip_orig; /**< @brief Static IP address pool original value given at command line. */
- const char *statip_help; /**< @brief Static IP address pool help description. */
- char * pcodns1_arg; /**< @brief PCO DNS Server 1 (default='0.0.0.0'). */
- char * pcodns1_orig; /**< @brief PCO DNS Server 1 original value given at command line. */
- const char *pcodns1_help; /**< @brief PCO DNS Server 1 help description. */
- char * pcodns2_arg; /**< @brief PCO DNS Server 2 (default='0.0.0.0'). */
- char * pcodns2_orig; /**< @brief PCO DNS Server 2 original value given at command line. */
- const char *pcodns2_help; /**< @brief PCO DNS Server 2 help description. */
- int timelimit_arg; /**< @brief Exit after timelimit seconds (default='0'). */
- char * timelimit_orig; /**< @brief Exit after timelimit seconds original value given at command line. */
- const char *timelimit_help; /**< @brief Exit after timelimit seconds help description. */
- char * apn_arg; /**< @brief Access point name (default='internet'). */
- char * apn_orig; /**< @brief Access point name original value given at command line. */
- const char *apn_help; /**< @brief Access point name help description. */
- int qos_arg; /**< @brief Requested quality of service (default='0x0b921f'). */
- char * qos_orig; /**< @brief Requested quality of service original value given at command line. */
- const char *qos_help; /**< @brief Requested quality of service help description. */
- 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 * 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. */
- int gtp_linux_flag; /**< @brief GTP linux kernel support (default=off). */
- const char *gtp_linux_help; /**< @brief GTP linux kernel support help description. */
-
- unsigned int help_given ; /**< @brief Whether help was given. */
- unsigned int version_given ; /**< @brief Whether version was given. */
- unsigned int fg_given ; /**< @brief Whether fg was given. */
- unsigned int debug_given ; /**< @brief Whether debug was given. */
- unsigned int conf_given ; /**< @brief Whether conf was given. */
- unsigned int pidfile_given ; /**< @brief Whether pidfile was given. */
- unsigned int statedir_given ; /**< @brief Whether statedir was given. */
- unsigned int listen_given ; /**< @brief Whether listen was given. */
- unsigned int net_given ; /**< @brief Whether net was given. */
- unsigned int ipup_given ; /**< @brief Whether ipup was given. */
- unsigned int ipdown_given ; /**< @brief Whether ipdown was given. */
- unsigned int dynip_given ; /**< @brief Whether dynip was given. */
- unsigned int statip_given ; /**< @brief Whether statip was given. */
- unsigned int pcodns1_given ; /**< @brief Whether pcodns1 was given. */
- unsigned int pcodns2_given ; /**< @brief Whether pcodns2 was given. */
- unsigned int timelimit_given ; /**< @brief Whether timelimit was given. */
- 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 loglevel_given ; /**< @brief Whether loglevel was given. */
- unsigned int gtp_linux_given ; /**< @brief Whether gtp-linux was given. */
-
-} ;
-
-/** @brief The additional parameters to pass to parser functions */
-struct cmdline_parser_params
-{
- int override; /**< @brief whether to override possibly already present options (default 0) */
- int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
- int check_required; /**< @brief whether to check that all required options were provided (default 1) */
- int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
- int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
-} ;
-
-/** @brief the purpose string of the program */
-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[];
-
-/**
- * The command line parser
- * @param argc the number of command line options
- * @param argv the command line options
- * @param args_info the structure where option information will be stored
- * @return 0 if everything went fine, NON 0 if an error took place
- */
-int cmdline_parser (int argc, char **argv,
- struct gengetopt_args_info *args_info);
-
-/**
- * The command line parser (version with additional parameters - deprecated)
- * @param argc the number of command line options
- * @param argv the command line options
- * @param args_info the structure where option information will be stored
- * @param override whether to override possibly already present options
- * @param initialize whether to initialize the option structure my_args_info
- * @param check_required whether to check that all required options were provided
- * @return 0 if everything went fine, NON 0 if an error took place
- * @deprecated use cmdline_parser_ext() instead
- */
-int cmdline_parser2 (int argc, char **argv,
- struct gengetopt_args_info *args_info,
- int override, int initialize, int check_required);
-
-/**
- * The command line parser (version with additional parameters)
- * @param argc the number of command line options
- * @param argv the command line options
- * @param args_info the structure where option information will be stored
- * @param params additional parameters for the parser
- * @return 0 if everything went fine, NON 0 if an error took place
- */
-int cmdline_parser_ext (int argc, char **argv,
- struct gengetopt_args_info *args_info,
- struct cmdline_parser_params *params);
-
-/**
- * Save the contents of the option struct into an already open FILE stream.
- * @param outfile the stream where to dump options
- * @param args_info the option struct to dump
- * @return 0 if everything went fine, NON 0 if an error took place
- */
-int cmdline_parser_dump(FILE *outfile,
- struct gengetopt_args_info *args_info);
-
-/**
- * Save the contents of the option struct into a (text) file.
- * This file can be read by the config file parser (if generated by gengetopt)
- * @param filename the file where to save
- * @param args_info the option struct to save
- * @return 0 if everything went fine, NON 0 if an error took place
- */
-int cmdline_parser_file_save(const char *filename,
- struct gengetopt_args_info *args_info);
-
-/**
- * Print the help
- */
-void cmdline_parser_print_help(void);
-/**
- * Print the version
- */
-void cmdline_parser_print_version(void);
-
-/**
- * Initializes all the fields a cmdline_parser_params structure
- * to their default values
- * @param params the structure to initialize
- */
-void cmdline_parser_params_init(struct cmdline_parser_params *params);
-
-/**
- * Allocates dynamically a cmdline_parser_params structure and initializes
- * all its fields to their default values
- * @return the created and initialized cmdline_parser_params structure
- */
-struct cmdline_parser_params *cmdline_parser_params_create(void);
-
-/**
- * Initializes the passed gengetopt_args_info structure's fields
- * (also set default values for options that have a default)
- * @param args_info the structure to initialize
- */
-void cmdline_parser_init (struct gengetopt_args_info *args_info);
-/**
- * Deallocates the string fields of the gengetopt_args_info structure
- * (but does not deallocate the structure itself)
- * @param args_info the structure to deallocate
- */
-void cmdline_parser_free (struct gengetopt_args_info *args_info);
-
-/**
- * The config file parser (deprecated version)
- * @param filename the name of the config file
- * @param args_info the structure where option information will be stored
- * @param override whether to override possibly already present options
- * @param initialize whether to initialize the option structure my_args_info
- * @param check_required whether to check that all required options were provided
- * @return 0 if everything went fine, NON 0 if an error took place
- * @deprecated use cmdline_parser_config_file() instead
- */
-int cmdline_parser_configfile (const char *filename,
- struct gengetopt_args_info *args_info,
- int override, int initialize, int check_required);
-
-/**
- * The config file parser
- * @param filename the name of the config file
- * @param args_info the structure where option information will be stored
- * @param params additional parameters for the parser
- * @return 0 if everything went fine, NON 0 if an error took place
- */
-int cmdline_parser_config_file (const char *filename,
- struct gengetopt_args_info *args_info,
- struct cmdline_parser_params *params);
-
-/**
- * Checks that all the required options were specified
- * @param args_info the structure to check
- * @param prog_name the name of the program that will be used to print
- * possible errors
- * @return
- */
-int cmdline_parser_required (struct gengetopt_args_info *args_info,
- const char *prog_name);
-
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* CMDLINE_H */
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index da6dd57..408827a 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -1,6 +1,7 @@
/*
* OpenGGSN - Gateway GPRS Support Node
* Copyright (C) 2002, 2003, 2004 Mondru AB.
+ * Copyright (C) 2017 by Harald Welte <laforge@gnumonks.org>
*
* The contents of this file may be used under the terms of the GNU
* General Public License Version 2, provided that the above copyright
@@ -19,42 +20,39 @@
#include "../config.h"
-#include <osmocom/core/application.h>
-
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <ctype.h>
-#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/ip6.h>
-#include <arpa/inet.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <fcntl.h>
#include <unistd.h>
#include <inttypes.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <net/if.h>
-
#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
-#include <time.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <osmocom/core/application.h>
#include <osmocom/core/select.h>
+#include <osmocom/core/stats.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/core/timer.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/control_cmd.h>
+#include <osmocom/ctrl/control_vty.h>
#include <osmocom/ctrl/ports.h>
+#include <osmocom/vty/telnet_interface.h>
+#include <osmocom/vty/logging.h>
+#include <osmocom/vty/stats.h>
+#include <osmocom/vty/ports.h>
+#include <osmocom/gsm/apn.h>
#include "../lib/tun.h"
#include "../lib/ippool.h"
@@ -62,82 +60,131 @@
#include "../lib/in46_addr.h"
#include "../gtp/pdp.h"
#include "../gtp/gtp.h"
-#include "cmdline.h"
#include "gtp-kernel.h"
#include "icmpv6.h"
+#include "ggsn.h"
+
+void *tall_ggsn_ctx;
-int end = 0;
-int maxfd = 0; /* For select() */
+static int end = 0;
+static int daemonize = 0;
+static struct ctrl_handle *g_ctrlh;
-struct in_addr listen_;
-struct in46_addr netaddr, destaddr, net; /* Network interface */
-size_t prefixlen;
-struct in46_addr dns1, dns2; /* PCO DNS address */
-char *ipup, *ipdown; /* Filename of scripts */
-int debug; /* Print debug output */
-struct ul255_t pco;
struct ul255_t qos;
struct ul255_t apn;
-struct gsn_t *gsn; /* GSN instance */
-struct tun_t *tun; /* TUN instance */
-struct ippool_t *ippool; /* Pool of IP addresses */
+#define LOGPAPN(subsys, level, apn, fmt, args...) \
+ LOGP(subsys, level, "APN %s: " fmt, (apn)->cfg.name, ## args)
-/* To exit gracefully. Used with GCC compilation flag -pg and gprof */
-void signal_handler(int s)
-{
- DEBUGP(DGGSN, "Received signal %d, exiting.\n", s);
- end = 1;
-}
+static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
+static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);
-/* Used to write process ID to file. Assume someone else will delete */
-void log_pid(char *pidfile)
+
+int apn_stop(struct apn_ctx *apn, bool force)
{
- FILE *file;
- mode_t oldmask;
+ LOGPAPN(DGGSN, LOGL_NOTICE, apn, "%sStopping\n", force ? "FORCED " : "");
+ /* FIXME: check if pools have any active PDP contexts and bail out */
+
+ /* shutdown whatever old state might be left */
+ if (apn->tun.tun) {
+ /* run ip-down script */
+ if (apn->tun.cfg.ipdown_script) {
+ LOGPAPN(DGGSN, LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
+ tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
+ }
+ /* release tun device */
+ LOGPAPN(DGGSN, LOGL_DEBUG, apn, "Closing TUN device\n");
+ osmo_fd_unregister(&apn->tun.fd);
+ tun_free(apn->tun.tun);
+ apn->tun.tun = NULL;
+ }
- oldmask = umask(022);
- file = fopen(pidfile, "w");
- umask(oldmask);
- if (!file) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Failed to create process ID file: %s!", pidfile);
- return;
+ if (apn->v4.pool) {
+ LOGPAPN(DGGSN, LOGL_DEBUG, apn, "Releasing IPv4 pool\n");
+ ippool_free(apn->v4.pool);
+ apn->v4.pool = NULL;
+ }
+ if (apn->v6.pool) {
+ LOGPAPN(DGGSN, LOGL_DEBUG, apn, "Releasing IPv6 pool\n");
+ ippool_free(apn->v6.pool);
+ apn->v6.pool = NULL;
}
- fprintf(file, "%d\n", (int)getpid());
- fclose(file);
+ return 0;
}
-#if defined(__sun__)
-int daemon(int nochdir, int noclose)
+/* actually start the APN with its current config */
+int apn_start(struct apn_ctx *apn)
{
- int fd;
+ LOGPAPN(DGGSN, LOGL_NOTICE, apn, "Starting\n");
+ switch (apn->cfg.gtpu_mode) {
+ case APN_GTPU_MODE_TUN:
+ //if (apn->tun.cfg.dev_name) {
+ LOGPAPN(DGGSN, LOGL_DEBUG, apn, "Opening TUN device\n");
+ if (tun_new(&apn->tun.tun)) {
+ LOGPAPN(DGGSN, LOGL_ERROR, apn, "Failed to configure tun device\n");
+ return -1;
+ }
- switch (fork()) {
- case -1:
- return (-1);
- case 0:
+ /* Register with libosmcoore */
+ osmo_fd_setup(&apn->tun.fd, apn->tun.tun->fd, BSC_FD_READ, ggsn_tun_fd_cb, apn, 0);
+ osmo_fd_register(&apn->tun.fd);
+
+ /* Set TUN library callback */
+ tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
+
+ if (apn->tun.cfg.do_ifconfig) {
+#if 0
+ DEBUGP(DGGSN, "Setting tun IP address\n");
+ if (tun_setaddr(tun, &netaddr, &destaddr, prefixlen)) {
+ SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to set tun IP address");
+ apn_stop(apn, false);
+ }
+#else
+ LOGPAPN(DGGSN, LOGL_ERROR, apn, "FIXME: ifconfig\n");
+#endif
+ }
+ if (apn->tun.cfg.ipup_script) {
+ LOGPAPN(DGGSN, LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipup_script);
+ tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
+ }
+ break;
+ case APN_GTPU_MODE_KERNEL_GTP:
+ LOGPAPN(DGGSN, LOGL_ERROR, apn, "FIXME: Kernel GTP\n");
+#if 0
+ /* use GTP kernel module for data packet encapsulation */
+ if (gtp_kernel_init(gsn, &net.v4, prefixlen, net_arg) < 0)
+ goto err;
+#endif
break;
default:
- _exit(0);
+ LOGPAPN(DGGSN, LOGL_ERROR, apn, "Unknown GTPU Mode %d\n", apn->cfg.gtpu_mode);
+ return -1;
}
- if (setsid() == -1)
- return (-1);
-
- if (!nochdir)
- chdir("/");
+ /* Create IPv4 pool */
+ if (apn->v4.cfg.static_prefix.addr.len) {
+ LOGPAPN(DGGSN, LOGL_DEBUG, apn, "Creating IPv4 pool\n");
+ if (ippool_new(&apn->v4.pool, &apn->v4.cfg.dynamic_prefix,
+ &apn->v4.cfg.static_prefix, 0)) {
+ LOGPAPN(DGGSN, LOGL_ERROR, apn, "Failed to create IPv4 pool\n");
+ apn_stop(apn, false);
+ return -1;
+ }
+ }
- if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
- dup2(fd, STDIN_FILENO);
- dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
- if (fd > 2)
- close(fd);
+ /* Create IPv6 pool */
+ if (apn->v6.cfg.static_prefix.addr.len) {
+ LOGPAPN(DGGSN, LOGL_DEBUG, apn, "Creating IPv6 pool\n");
+ if (ippool_new(&apn->v6.pool, &apn->v6.cfg.dynamic_prefix,
+ &apn->v6.cfg.static_prefix, 0)) {
+ LOGPAPN(DGGSN, LOGL_ERROR, apn, "Failed to create IPv6 pool\n");
+ apn_stop(apn, false);
+ return -1;
+ }
}
- return (0);
+
+ return 0;
}
-#endif
static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const struct ippoolm_t *member, const char *var)
{
@@ -148,21 +195,24 @@ static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const st
snprintf(val, sizeof(val), "%" PRIu64 ",%s", pdp->imsi, addrstr);
- if (ctrl_cmd_send_trap(gsn->ctrl, var, val) < 0) {
+ if (ctrl_cmd_send_trap(g_ctrlh, var, val) < 0) {
LOGP(DGGSN, LOGL_ERROR, "Failed to create and send TRAP for IMSI %" PRIu64 " [%s].\n", pdp->imsi, var);
return false;
}
return true;
}
-int delete_context(struct pdp_t *pdp)
+static int delete_context(struct pdp_t *pdp)
{
+ struct gsn_t *gsn = pdp->gsn;
+ struct ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer;
+
DEBUGP(DGGSN, "Deleting PDP context\n");
struct ippoolm_t *member = pdp->peer;
if (pdp->peer) {
send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
- ippool_freeip(ippool, (struct ippoolm_t *)pdp->peer);
+ ippool_freeip(ipp->pool, ipp);
} else
SYS_ERR(DGGSN, LOGL_ERROR, 0, "Peer not defined!");
@@ -236,31 +286,74 @@ static bool pdp_has_v4(struct pdp_t *pdp)
return false;
}
+/* construct an IPCP PCO from up to two given DNS addreses */
+static int build_ipcp_pco(struct msgb *msg, uint8_t id, const struct in46_addr *dns1,
+ const struct in46_addr *dns2)
+{
+ uint8_t *len1, *len2;
+ uint8_t *start = msg->tail;
+ unsigned int len_appended;
+
+ /* Three byte T16L header */
+ msgb_put_u16(msg, 0x8021); /* IPCP */
+ len1 = msgb_put(msg, 1); /* Length of contents: delay */
+
+ msgb_put_u8(msg, 0x02); /* ACK */
+ msgb_put_u8(msg, id); /* ID: Needs to match request */
+ msgb_put_u8(msg, 0x00); /* Length MSB */
+ len2 = msgb_put(msg, 1); /* Length LSB: delay */
+
+ if (dns1 && dns1->len == 4) {
+ msgb_put(msg, 0x81); /* DNS1 Tag */
+ msgb_put(msg, 2 + dns1->len); /* DNS1 Length, incl. TL */
+ msgb_put_u32(msg, dns1->v4.s_addr);
+ }
+
+ if (dns2 && dns2->len == 4) {
+ msgb_put(msg, 0x83); /* DNS2 Tag */
+ msgb_put(msg, 2 + dns2->len); /* DNS2 Length, incl. TL */
+ msgb_put_u32(msg, dns2->v4.s_addr);
+ }
+
+ /* patch in length values */
+ len_appended = msg->tail - start;
+ *len1 = len_appended - 3;
+ *len2 = len_appended - 3;
+
+ return 0;
+}
+
/* process one PCO request from a MS/UE, putting together the proper responses */
-static void process_pco(struct pdp_t *pdp)
+static void process_pco(struct apn_ctx *apn, struct pdp_t *pdp)
{
struct msgb *msg = msgb_alloc(256, "PCO");
+ unsigned int i;
+
+ OSMO_ASSERT(msg);
msgb_put_u8(msg, 0x80); /* ext-bit + configuration protocol byte */
/* FIXME: also check if primary / secondary DNS was requested */
if (pdp_has_v4(pdp) && pco_contains_proto(&pdp->pco_req, PCO_P_IPCP)) {
/* FIXME: properly implement this for IPCP */
- uint8_t *cur = msgb_put(msg, pco.l-1);
- memcpy(cur, pco.v+1, pco.l-1);
+ build_ipcp_pco(msg, 0, &apn->v4.cfg.dns[0], &apn->v4.cfg.dns[1]);
}
if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv6_ADDR)) {
- if (dns1.len == 16)
- msgb_t16lv_put(msg, PCO_P_DNS_IPv6_ADDR, dns1.len, dns1.v6.s6_addr);
- if (dns2.len == 16)
- msgb_t16lv_put(msg, PCO_P_DNS_IPv6_ADDR, dns2.len, dns2.v6.s6_addr);
+ for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
+ struct in46_addr *i46a = &apn->v6.cfg.dns[i];
+ if (i46a->len != 16)
+ continue;
+ msgb_t16lv_put(msg, PCO_P_DNS_IPv6_ADDR, i46a->len, i46a->v6.s6_addr);
+ }
}
if (pco_contains_proto(&pdp->pco_req, PCO_P_DNS_IPv4_ADDR)) {
- if (dns1.len == 4)
- msgb_t16lv_put(msg, PCO_P_DNS_IPv4_ADDR, dns1.len, (uint8_t *)&dns1.v4);
- if (dns2.len == 4)
- msgb_t16lv_put(msg, PCO_P_DNS_IPv4_ADDR, dns2.len, (uint8_t *)&dns2.v4);
+ for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
+ struct in46_addr *i46a = &apn->v4.cfg.dns[i];
+ if (i46a->len != 4)
+ continue;
+ msgb_t16lv_put(msg, PCO_P_DNS_IPv4_ADDR, i46a->len, (uint8_t *)&i46a->v4);
+ }
}
if (msgb_length(msg) > 1) {
@@ -274,11 +367,28 @@ static void process_pco(struct pdp_t *pdp)
int create_context_ind(struct pdp_t *pdp)
{
+ static char name_buf[256];
+ struct gsn_t *gsn = pdp->gsn;
+ struct ggsn_ctx *ggsn = gsn->priv;
struct in46_addr addr;
struct ippoolm_t *member;
+ struct apn_ctx *apn;
int rc;
- DEBUGP(DGGSN, "Received create PDP context request\n");
+ osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
+
+ DEBUGP(DGGSN, "Received create PDP context request for APN '%s'\n", name_buf);
+
+ /* First find an exact APN name match */
+ apn = ggsn_find_apn(ggsn, name_buf);
+ /* then try default (if any) */
+ if (!apn)
+ apn = ggsn->cfg.default_apn;
+ if (!apn) {
+ /* no APN found for what user requested */
+ gtp_create_context_resp(gsn, pdp, GTPCAUSE_MISSING_APN);
+ return 0;
+ }
/* FIXME: we manually force all context requests to dynamic here! */
if (pdp->eua.l > 2)
@@ -296,15 +406,25 @@ int create_context_ind(struct pdp_t *pdp)
return 0;
}
- rc = ippool_newip(ippool, &member, &addr, 0);
- if (rc < 0) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0, "Cannot allocate IP address in pool\n");
- gtp_create_context_resp(gsn, pdp, -rc);
- return 0; /* Allready in use, or no more available */
- }
+ if (addr.len == sizeof(struct in_addr)) {
+ rc = ippool_newip(apn->v4.pool, &member, &addr, 0);
+ if (rc < 0)
+ goto err_pool_full;
+ in46a_to_eua(&member->addr, &pdp->eua);
- if (addr.len == sizeof(struct in6_addr)) {
+ /* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
+ if (gtp_kernel_tunnel_add(pdp) < 0) {
+ SYS_ERR(DGGSN, LOGL_ERROR, 0,
+ "Cannot add tunnel to kernel: %s\n", strerror(errno));
+ gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
+ return 0;
+ }
+ } else if (addr.len == sizeof(struct in6_addr)) {
struct in46_addr tmp;
+ rc = ippool_newip(apn->v6.pool, &member, &addr, 0);
+ if (rc < 0)
+ goto err_pool_full;
+
/* IPv6 doesn't really send the real/allocated address at this point, but just
* the link-identifier which the MS shall use for router solicitation */
tmp.len = addr.len;
@@ -314,43 +434,44 @@ int create_context_ind(struct pdp_t *pdp)
memcpy(tmp.v6.s6_addr+8, &member->addr.v6, 8);
in46a_to_eua(&tmp, &pdp->eua);
} else
- in46a_to_eua(&member->addr, &pdp->eua);
+ OSMO_ASSERT(0);
+
pdp->peer = member;
- pdp->ipif = tun; /* TODO */
+ pdp->ipif = apn->tun.tun; /* TODO */
member->peer = pdp;
- /* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
- if (gtp_kernel_tunnel_add(pdp) < 0) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Cannot add tunnel to kernel: %s\n", strerror(errno));
- gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
- return 0;
- }
-
if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
return 0;
}
- process_pco(pdp);
+ process_pco(apn, pdp);
gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ);
return 0; /* Success */
+
+err_pool_full:
+ SYS_ERR(DGGSN, LOGL_ERROR, 0, "Cannot allocate IP address in pool\n");
+ gtp_create_context_resp(gsn, pdp, -rc);
+ return 0; /* Already in use, or no more available */
}
-/* Callback for receiving messages from tun */
-int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
+/* Internet-originated IP packet, needs to be sent via GTP towards MS */
+static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
{
+ struct apn_ctx *apn = tun->priv;
struct ippoolm_t *ipm;
struct in46_addr dst;
struct iphdr *iph = (struct iphdr *)pack;
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
+ struct ippool_t *pool;
if (iph->version == 4) {
if (len < sizeof(*iph) || len < 4*iph->ihl)
return -1;
dst.len = 4;
dst.v4.s_addr = iph->daddr;
+ pool = apn->v4.pool;
} else if (iph->version == 6) {
/* Due to the fact that 3GPP requires an allocation of a
* /64 prefix to each MS, we must instruct
@@ -358,6 +479,7 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
* prefix, i.e. the first 8 bytes of the address */
dst.len = 8;
dst.v6 = ip6h->ip6_dst;
+ pool = apn->v6.pool;
} else {
LOGP(DGGSN, LOGL_NOTICE, "non-IPv packet received from tun\n");
return -1;
@@ -365,13 +487,13 @@ int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
DEBUGP(DGGSN, "Received packet from tun!\n");
- if (ippool_getip(ippool, &ipm, &dst)) {
+ if (ippool_getip(pool, &ipm, &dst)) {
DEBUGP(DGGSN, "Received packet with no destination!!!\n");
return 0;
}
if (ipm->peer) /* Check if a peer protocol is defined */
- gtp_data_req(gsn, (struct pdp_t *)ipm->peer, pack, len);
+ gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len);
return 0;
}
@@ -380,7 +502,8 @@ static const struct in6_addr all_router_mcast_addr = {
.s6_addr = { 0xff,0x02,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,2 }
};
-int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
+/* MS-originated GTP1-U packet, needs to be sent via TUN device */
+static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
{
struct iphdr *iph = (struct iphdr *)pack;
struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
@@ -391,7 +514,7 @@ int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
case 6:
/* daddr: all-routers multicast addr */
if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
- return handle_router_mcast(gsn, pdp, pack, len);
+ return handle_router_mcast(pdp->gsn, pdp, pack, len);
break;
case 4:
break;
@@ -402,410 +525,201 @@ int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
}
-int main(int argc, char **argv)
+static char *config_file = "openggsn.cfg";
+
+/* callback for tun device osmocom select loop integration */
+static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what)
{
- /* gengeopt declarations */
- struct gengetopt_args_info args_info;
+ struct apn_ctx *apn = fd->data;
- struct hostent *host;
+ OSMO_ASSERT(what & BSC_FD_READ);
- /* Handle keyboard interrupt SIGINT */
- struct sigaction s;
- s.sa_handler = (void *)signal_handler;
- if ((0 != sigemptyset(&s.sa_mask)) && debug)
- printf("sigemptyset failed.\n");
- s.sa_flags = SA_RESETHAND;
- if ((sigaction(SIGINT, &s, NULL) != 0) && debug)
- printf("Could not register SIGINT signal handler.\n");
-
- fd_set fds; /* For select() */
- struct timeval idleTime; /* How long to select() */
+ return tun_decaps(apn->tun.tun);
+}
- int timelimit; /* Number of seconds to be connected */
- int starttime; /* Time program was started */
+/* callback for libgtp osmocom select loop integration */
+static int ggsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
+{
+ struct ggsn_ctx *ggsn = fd->data;
+ int rc;
- osmo_init_logging(&log_info);
+ OSMO_ASSERT(what & BSC_FD_READ);
- if (cmdline_parser(argc, argv, &args_info) != 0)
- exit(1);
- if (args_info.debug_flag) {
- printf("listen: %s\n", args_info.listen_arg);
- if (args_info.conf_arg)
- printf("conf: %s\n", args_info.conf_arg);
- printf("fg: %d\n", args_info.fg_flag);
- printf("debug: %d\n", args_info.debug_flag);
- printf("qos: %#08x\n", args_info.qos_arg);
- if (args_info.apn_arg)
- printf("apn: %s\n", args_info.apn_arg);
- if (args_info.net_arg)
- printf("net: %s\n", args_info.net_arg);
- if (args_info.dynip_arg)
- printf("dynip: %s\n", args_info.dynip_arg);
- if (args_info.statip_arg)
- printf("statip: %s\n", args_info.statip_arg);
- if (args_info.ipup_arg)
- printf("ipup: %s\n", args_info.ipup_arg);
- if (args_info.ipdown_arg)
- printf("ipdown: %s\n", args_info.ipdown_arg);
- if (args_info.pidfile_arg)
- printf("pidfile: %s\n", args_info.pidfile_arg);
- if (args_info.statedir_arg)
- printf("statedir: %s\n", args_info.statedir_arg);
- if (args_info.gtp_linux_flag)
- printf("gtp_linux: %d\n", args_info.gtp_linux_flag);
- printf("timelimit: %d\n", args_info.timelimit_arg);
+ switch (fd->priv_nr) {
+ case 0:
+ rc = gtp_decaps0(ggsn->gsn);
+ break;
+ case 1:
+ rc = gtp_decaps1c(ggsn->gsn);
+ break;
+ case 2:
+ rc = gtp_decaps1u(ggsn->gsn);
+ break;
+ default:
+ rc = -EINVAL;
+ break;
}
+ return rc;
+}
- /* Try out our new parser */
+static void ggsn_gtp_tmr_start(struct ggsn_ctx *ggsn)
+{
+ struct timeval next;
- if (cmdline_parser_configfile(args_info.conf_arg, &args_info, 0, 0, 0)
- != 0)
- exit(1);
+ /* Retrieve next retransmission as timeval */
+ gtp_retranstimeout(ggsn->gsn, &next);
- /* Open a log file */
- if (args_info.logfile_arg) {
- struct log_target *tgt;
- int lvl;
-
- tgt = log_target_find(LOG_TGT_TYPE_FILE, args_info.logfile_arg);
- if (!tgt) {
- tgt = log_target_create_file(args_info.logfile_arg);
- if (!tgt) {
- LOGP(DGGSN, LOGL_ERROR,
- "Failed to create logfile: %s\n",
- args_info.logfile_arg);
- exit(1);
- }
- log_add_target(tgt);
- }
- log_set_all_filter(tgt, 1);
- log_set_use_color(tgt, 0);
-
- if (args_info.loglevel_arg) {
- lvl = log_parse_level(args_info.loglevel_arg);
- log_set_log_level(tgt, lvl);
- LOGP(DGGSN, LOGL_NOTICE,
- "Set file log level to %s\n",
- log_level_str(lvl));
- }
- }
+ /* re-schedule the timer */
+ osmo_timer_schedule(&ggsn->gtp_timer, next.tv_sec, next.tv_usec/1000);
+}
- if (args_info.debug_flag) {
- printf("cmdline_parser_configfile\n");
- printf("listen: %s\n", args_info.listen_arg);
- printf("conf: %s\n", args_info.conf_arg);
- printf("fg: %d\n", args_info.fg_flag);
- printf("debug: %d\n", args_info.debug_flag);
- printf("qos: %#08x\n", args_info.qos_arg);
- if (args_info.apn_arg)
- printf("apn: %s\n", args_info.apn_arg);
- if (args_info.net_arg)
- printf("net: %s\n", args_info.net_arg);
- if (args_info.dynip_arg)
- printf("dynip: %s\n", args_info.dynip_arg);
- if (args_info.statip_arg)
- printf("statip: %s\n", args_info.statip_arg);
- if (args_info.ipup_arg)
- printf("ipup: %s\n", args_info.ipup_arg);
- if (args_info.ipdown_arg)
- printf("ipdown: %s\n", args_info.ipdown_arg);
- if (args_info.pidfile_arg)
- printf("pidfile: %s\n", args_info.pidfile_arg);
- if (args_info.statedir_arg)
- printf("statedir: %s\n", args_info.statedir_arg);
- if (args_info.gtp_linux_flag)
- printf("gtp-linux: %d\n", args_info.gtp_linux_flag);
- printf("timelimit: %d\n", args_info.timelimit_arg);
- }
+/* timer callback for libgtp retransmission and ping */
+static void ggsn_gtp_tmr_cb(void *data)
+{
+ struct ggsn_ctx *ggsn = data;
- /* Handle each option */
+ /* do all the retransmissions as needed */
+ gtp_retrans(ggsn->gsn);
- /* debug */
- debug = args_info.debug_flag;
+ ggsn_gtp_tmr_start(ggsn);
+}
- /* listen */
- /* Do hostname lookup to translate hostname to IP address */
- /* Any port listening is not possible as a valid address is */
- /* required for create_pdp_context_response messages */
- if (args_info.listen_arg) {
- if (!(host = gethostbyname(args_info.listen_arg))) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Invalid listening address: %s!",
- args_info.listen_arg);
- exit(1);
- } else {
- memcpy(&listen_.s_addr, host->h_addr, host->h_length);
- }
- } else {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Listening address must be specified! "
- "Please use command line option --listen or "
- "edit %s configuration file\n", args_info.conf_arg);
- exit(1);
- }
+/* To exit gracefully. Used with GCC compilation flag -pg and gprof */
+static void signal_handler(int s)
+{
+ DEBUGP(DGGSN, "Received signal %d, exiting.\n", s);
+ end = 1;
+}
- /* net */
- /* Store net as in_addr net and mask */
- if (args_info.net_arg) {
- if (ippool_aton(&net, &prefixlen, args_info.net_arg, 0)) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Invalid network address: %s!",
- args_info.net_arg);
- exit(1);
- }
- /* default for network + destination address = net + 1 */
- netaddr = net;
- in46a_inc(&netaddr);
- destaddr = netaddr;
- } else {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Network address must be specified: %s!",
- args_info.net_arg);
- exit(1);
- }
- /* dynip */
- if (!args_info.dynip_arg) {
- if (ippool_new(&ippool, args_info.net_arg, NULL, 1, 0,
- IPPOOL_NONETWORK | IPPOOL_NOGATEWAY |
- IPPOOL_NOBROADCAST)) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Failed to allocate IP pool!");
- exit(1);
- }
- } else {
- if (ippool_new(&ippool, args_info.dynip_arg, NULL, 1, 0,
- IPPOOL_NONETWORK | IPPOOL_NOGATEWAY |
- IPPOOL_NOBROADCAST)) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Failed to allocate IP pool!");
- exit(1);
- }
- }
+/* Start a given GGSN */
+int ggsn_start(struct ggsn_ctx *ggsn)
+{
+ int rc;
- /* DNS1 and DNS2 */
- memset(&dns1, 0, sizeof(dns1));
- if (args_info.pcodns1_arg) {
- size_t tmp;
- if (ippool_aton(&dns1, &tmp, args_info.pcodns1_arg, 0) != 0) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Failed to convert pcodns1!");
- exit(1);
- }
- }
- memset(&dns2, 0, sizeof(dns2));
- if (args_info.pcodns2_arg) {
- size_t tmp;
- if (ippool_aton(&dns2, &tmp, args_info.pcodns2_arg, 0) != 0) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "Failed to convert pcodns2!");
- exit(1);
- }
+ /* Start libgtp listener */
+ if (gtp_new(&ggsn->gsn, ggsn->cfg.state_dir, &ggsn->cfg.listen_addr.v4, GTP_MODE_GGSN)) {
+ SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to create GTP");
+ return -1;
}
- unsigned int cur = 0;
- pco.v[cur++] = 0x80; /* x0000yyy x=1, yyy=000: PPP */
- pco.v[cur++] = 0x80; /* IPCP */
- pco.v[cur++] = 0x21;
- pco.v[cur++] = 0xFF; /* Length of contents */
- pco.v[cur++] = 0x02; /* ACK */
- pco.v[cur++] = 0x00; /* ID: Need to match request */
- pco.v[cur++] = 0x00; /* Length */
- pco.v[cur++] = 0xFF; /* overwritten */
- if (dns1.len == 4) {
- pco.v[cur++] = 0x81; /* DNS 1 */
- pco.v[cur++] = 2 + dns1.len;
- if (dns1.len == 4)
- memcpy(&pco.v[cur], &dns1.v4, dns1.len);
- else
- memcpy(&pco.v[cur], &dns1.v6, dns1.len);
- cur += dns1.len;
- }
- if (dns2.len == 4) {
- pco.v[cur++] = 0x83;
- pco.v[cur++] = 2 + dns2.len; /* DNS 2 */
- if (dns2.len == 4)
- memcpy(&pco.v[cur], &dns2.v4, dns2.len);
- else
- memcpy(&pco.v[cur], &dns2.v6, dns2.len);
- cur += dns2.len;
- }
- pco.l = cur;
- /* patch in length values */
- pco.v[3] = pco.l - 4;
- pco.v[7] = pco.l - 4;
+ /* Register File Descriptors */
+ osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
+ rc = osmo_fd_register(&ggsn->gtp_fd0);
+ OSMO_ASSERT(rc == 0);
- /* ipup */
- ipup = args_info.ipup_arg;
+ osmo_fd_setup(&ggsn->gtp_fd1c, ggsn->gsn->fd1c, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 1);
+ rc = osmo_fd_register(&ggsn->gtp_fd1c);
+ OSMO_ASSERT(rc == 0);
- /* ipdown */
- ipdown = args_info.ipdown_arg;
+ osmo_fd_setup(&ggsn->gtp_fd1u, ggsn->gsn->fd1u, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 2);
+ rc = osmo_fd_register(&ggsn->gtp_fd1u);
+ OSMO_ASSERT(rc == 0);
- /* Timelimit */
- timelimit = args_info.timelimit_arg;
- starttime = time(NULL);
+ /* Start GTP re-transmission timer */
+ osmo_timer_setup(&ggsn->gtp_timer, ggsn_gtp_tmr_cb, ggsn);
- /* qos */
- qos.l = 3;
- qos.v[2] = (args_info.qos_arg) & 0xff;
- qos.v[1] = ((args_info.qos_arg) >> 8) & 0xff;
- qos.v[0] = ((args_info.qos_arg) >> 16) & 0xff;
+ gtp_set_cb_data_ind(ggsn->gsn, encaps_tun);
+ gtp_set_cb_delete_context(ggsn->gsn, delete_context);
+ gtp_set_cb_create_context_ind(ggsn->gsn, create_context_ind);
- /* apn */
- if (strlen(args_info.apn_arg) > (sizeof(apn.v) - 1)) {
- LOGP(DGGSN, LOGL_ERROR, "Invalid APN\n");
- return -1;
- }
- apn.l = strlen(args_info.apn_arg) + 1;
- apn.v[0] = (char)strlen(args_info.apn_arg);
- strncpy((char *)&apn.v[1], args_info.apn_arg, sizeof(apn.v) - 1);
-
- /* foreground */
- /* If flag not given run as a daemon */
- if (!args_info.fg_flag) {
- FILE *f;
- int rc;
- /* Close the standard file descriptors. */
- /* Is this really needed ? */
- f = freopen("/dev/null", "w", stdout);
- if (f == NULL) {
- SYS_ERR(DGGSN, LOGL_NOTICE, 0,
- "Could not redirect stdout to /dev/null");
- }
- f = freopen("/dev/null", "w", stderr);
- if (f == NULL) {
- SYS_ERR(DGGSN, LOGL_NOTICE, 0,
- "Could not redirect stderr to /dev/null");
- }
- f = freopen("/dev/null", "r", stdin);
- if (f == NULL) {
- SYS_ERR(DGGSN, LOGL_NOTICE, 0,
- "Could not redirect stdin to /dev/null");
- }
- rc = daemon(0, 0);
- if (rc != 0) {
- SYS_ERR(DGGSN, LOGL_ERROR, rc,
- "Could not daemonize");
- exit(1);
- }
- }
+ return 0;
+}
- /* pidfile */
- /* This has to be done after we have our final pid */
- if (args_info.pidfile_arg) {
- log_pid(args_info.pidfile_arg);
- }
+/* Stop a given GGSN */
+int ggsn_stop(struct ggsn_ctx *ggsn)
+{
+ struct apn_ctx *apn;
- DEBUGP(DGGSN, "gtpclient: Initialising GTP tunnel\n");
+ /* iterate over all APNs and stop them */
+ llist_for_each_entry(apn, &ggsn->apn_list, list)
+ apn_stop(apn, true);
- if (gtp_new(&gsn, args_info.statedir_arg, &listen_, GTP_MODE_GGSN)) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to create gtp");
- exit(1);
- }
- if (gsn->fd0 > maxfd)
- maxfd = gsn->fd0;
- if (gsn->fd1c > maxfd)
- maxfd = gsn->fd1c;
- if (gsn->fd1u > maxfd)
- maxfd = gsn->fd1u;
-
- /* use GTP kernel module for data packet encapsulation */
- if (gtp_kernel_init(gsn, &net.v4, prefixlen, &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);
-
- gsn->ctrl = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_GGSN, NULL);
- if (!gsn->ctrl) {
- LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
- exit(1);
- }
+ osmo_timer_del(&ggsn->gtp_timer);
- /* skip the configuration of the tun0 if we're using the gtp0 device */
- if (gtp_kernel_enabled())
- goto skip_tun;
+ osmo_fd_unregister(&ggsn->gtp_fd1u);
+ osmo_fd_unregister(&ggsn->gtp_fd1c);
+ osmo_fd_unregister(&ggsn->gtp_fd0);
- /* Create a tunnel interface */
- DEBUGP(DGGSN, "Creating tun interface\n");
- if (tun_new((struct tun_t **)&tun)) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to create tun");
- exit(1);
+ if (ggsn->gsn) {
+ gtp_free(ggsn->gsn);
+ ggsn->gsn = NULL;
}
-#if 0
- DEBUGP(DGGSN, "Setting tun IP address\n");
- if (tun_setaddr(tun, &netaddr, &destaddr, prefixlen)) {
- SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to set tun IP address");
- exit(1);
- }
-#endif
+ return 0;
+}
+static void handle_options(int argc, char **argv)
+{
+ /* FIXME */
+}
- tun_set_cb_ind(tun, cb_tun_ind);
- if (tun->fd > maxfd)
- maxfd = tun->fd;
+int main(int argc, char **argv)
+{
+ struct ggsn_ctx *ggsn;
+ int rc;
- if (ipup)
- tun_runscript(tun, ipup);
+ /* Handle keyboard interrupt SIGINT */
+ tall_ggsn_ctx = talloc_named_const(NULL, 0, "openggsn");
+ msgb_talloc_ctx_init(tall_ggsn_ctx, 0);
-skip_tun:
+ signal(SIGINT, &signal_handler);
+ signal(SIGABRT, &signal_handler);
+ signal(SIGUSR1, &signal_handler);
+ signal(SIGUSR2, &signal_handler);
- /******************************************************************/
- /* Main select loop */
- /******************************************************************/
+ osmo_init_ignore_signals();
+ osmo_init_logging(&log_info);
+ osmo_stats_init(tall_ggsn_ctx);
- while ((((starttime + timelimit) > time(NULL)) || (0 == timelimit))
- && (!end)) {
+ vty_init(&g_vty_info);
+ logging_vty_add_cmds(NULL);
+ osmo_stats_vty_add_cmds(&log_info);
+ ggsn_vty_init();
+ ctrl_vty_init(tall_ggsn_ctx);
- FD_ZERO(&fds);
- if (tun)
- FD_SET(tun->fd, &fds);
- FD_SET(gsn->fd0, &fds);
- FD_SET(gsn->fd1c, &fds);
- FD_SET(gsn->fd1u, &fds);
+ handle_options(argc, argv);
- gtp_retranstimeout(gsn, &idleTime);
- switch (select(maxfd + 1, &fds, NULL, NULL, &idleTime)) {
- case -1: /* errno == EINTR : unblocked signal */
- SYS_ERR(DGGSN, LOGL_ERROR, 0,
- "select() returned -1");
- /* On error, select returns without modifying fds */
- FD_ZERO(&fds);
- break;
- case 0:
- /* printf("Select returned 0\n"); */
- gtp_retrans(gsn); /* Only retransmit if nothing else */
- break;
- default:
- break;
- }
+ rate_ctr_init(tall_ggsn_ctx);
- 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);
- }
+ rc = vty_read_config_file(config_file, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to open config file: '%s'\n", config_file);
+ exit(2);
+ }
+
+ rc = telnet_init_dynif(tall_ggsn_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_GGSN);
+ if (rc < 0)
+ exit(1);
- if (FD_ISSET(gsn->fd0, &fds))
- gtp_decaps0(gsn);
+ g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_GGSN, NULL);
+ if (!g_ctrlh) {
+ LOGP(DGGSN, LOGL_ERROR, "Failed to create CTRL interface.\n");
+ exit(1);
+ }
- if (FD_ISSET(gsn->fd1c, &fds))
- gtp_decaps1c(gsn);
+ if (daemonize) {
+ rc = osmo_daemonize();
+ if (rc < 0) {
+ perror("Error during daemonize");
+ exit(1);
+ }
+ }
- if (FD_ISSET(gsn->fd1u, &fds))
- gtp_decaps1u(gsn);
+#if 0
+ /* qos */
+ qos.l = 3;
+ qos.v[2] = (args_info.qos_arg) & 0xff;
+ qos.v[1] = ((args_info.qos_arg) >> 8) & 0xff;
+ qos.v[0] = ((args_info.qos_arg) >> 16) & 0xff;
+#endif
- osmo_select_main(1);
+ /* Main select loop */
+ while (!end) {
+ osmo_select_main(0);
}
-err:
- gtp_kernel_stop();
- cmdline_parser_free(&args_info);
- ippool_free(ippool);
- gtp_free(gsn);
- if (tun)
- tun_free(tun);
- return 1;
+ llist_for_each_entry(ggsn, &g_ggsn_list, list)
+ ggsn_stop(ggsn);
+ return 1;
}
diff --git a/ggsn/ggsn.h b/ggsn/ggsn.h
new file mode 100644
index 0000000..4ad0052
--- /dev/null
+++ b/ggsn/ggsn.h
@@ -0,0 +1,130 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/timer.h>
+
+#include "../lib/tun.h"
+#include "../lib/ippool.h"
+#include "../lib/syserr.h"
+#include "../lib/in46_addr.h"
+#include "../gtp/gtp.h"
+
+#define APN_TYPE_IPv4 0x01 /* v4-only */
+#define APN_TYPE_IPv6 0x02 /* v6-only */
+#define APN_TYPE_IPv4v6 0x04 /* v4v6 dual-stack */
+
+struct ggsn_ctx;
+
+struct apn_ctx_ip {
+ struct {
+ struct in46_prefix static_prefix;
+ struct in46_prefix dynamic_prefix;
+ /* v4 DNS server names */
+ struct in46_addr dns[2];
+ } cfg;
+
+ /* v4 address pool */
+ struct ippool_t *pool;
+};
+
+struct apn_name {
+ struct llist_head list;
+ char *name;
+};
+
+enum apn_gtpu_mode {
+ APN_GTPU_MODE_TUN = 0, /* default */
+ APN_GTPU_MODE_KERNEL_GTP,
+};
+
+struct apn_ctx {
+ /* list of APNs inside GGSN */
+ struct llist_head list;
+ /* back-pointer to GGSN */
+ struct ggsn_ctx *ggsn;
+
+ struct {
+ /* Primary name */
+ char *name;
+ /* Description string */
+ char *description;
+ /* List of secondary APN names */
+ struct llist_head name_list;
+ /* types supported address types on this APN */
+ uint32_t apn_type_mask;
+ /* GTP-U via TUN device or in Linux kernel */
+ enum apn_gtpu_mode gtpu_mode;
+ /* administratively shut-down (true) or not (false) */
+ bool shutdown;
+ } cfg;
+
+ /* corresponding tun device */
+ struct {
+ struct {
+ /* name of the network device */
+ char *dev_name;
+ /* should we try to configure it? */
+ bool do_ifconfig;
+ /* ip-up and ip-down script names/paths */
+ char *ipup_script;
+ char *ipdown_script;
+ } cfg;
+ struct tun_t *tun;
+ struct osmo_fd fd;
+ } tun;
+
+ struct apn_ctx_ip v4;
+ struct apn_ctx_ip v6;
+};
+
+struct ggsn_ctx {
+ /* global list of GGSNs */
+ struct llist_head list;
+
+ /* list of APNs in this GGSN */
+ struct llist_head apn_list;
+
+ struct {
+ char *name;
+ /* Description string */
+ char *description;
+ /* an APN that shall be used as default for any non-matching APN */
+ struct apn_ctx *default_apn;
+ /* ADdress to which we listen for GTP */
+ struct in46_addr listen_addr;
+ /* directory for state file */
+ char *state_dir;
+ /* administratively shut-down (true) or not (false) */
+ bool shutdown;
+ } cfg;
+
+ /* The libgtp (G)GSN instance, i.e. what listens to GTP */
+ struct gsn_t *gsn;
+
+ /* osmo-fd for gsn */
+ struct osmo_fd gtp_fd0;
+ struct osmo_fd gtp_fd1c;
+ struct osmo_fd gtp_fd1u;
+
+ struct osmo_timer_list gtp_timer;
+};
+
+/* ggsn_vty.c */
+extern struct llist_head g_ggsn_list;
+extern struct vty_app_info g_vty_info;
+extern int ggsn_vty_init(void);
+struct ggsn_ctx *ggsn_find(const char *name);
+struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name);
+struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name);
+struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name);
+
+/* ggsn.c */
+extern void *tall_ggsn_ctx;
+extern int ggsn_start(struct ggsn_ctx *ggsn);
+extern int ggsn_stop(struct ggsn_ctx *ggsn);
+extern int apn_start(struct apn_ctx *apn);
+extern int apn_stop(struct apn_ctx *apn, bool force);
diff --git a/ggsn/ggsn_vty.c b/ggsn/ggsn_vty.c
new file mode 100644
index 0000000..a3e1ee9
--- /dev/null
+++ b/ggsn/ggsn_vty.c
@@ -0,0 +1,788 @@
+/*
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/rate_ctr.h>
+#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/misc.h>
+
+#include "../gtp/gtp.h"
+#include "../gtp/pdp.h"
+
+#include "ggsn.h"
+
+#define PREFIX_STR "Prefix (Network/Netmask)\n"
+
+LLIST_HEAD(g_ggsn_list);
+
+enum ggsn_vty_node {
+ GGSN_NODE = _LAST_OSMOVTY_NODE + 1,
+ APN_NODE,
+};
+
+struct ggsn_ctx *ggsn_find(const char *name)
+{
+ struct ggsn_ctx *ggsn;
+
+ llist_for_each_entry(ggsn, &g_ggsn_list, list) {
+ if (!strcmp(ggsn->cfg.name, name))
+ return ggsn;
+ }
+ return NULL;
+}
+
+struct ggsn_ctx *ggsn_find_or_create(void *ctx, const char *name)
+{
+ struct ggsn_ctx *ggsn;
+
+ ggsn = ggsn_find(name);
+ if (ggsn)
+ return ggsn;
+
+ ggsn = talloc_zero(ctx, struct ggsn_ctx);
+ if (!ggsn)
+ return NULL;
+
+ ggsn->cfg.name = talloc_strdup(ggsn, name);
+ ggsn->cfg.state_dir = talloc_strdup(ggsn, "/tmp");
+ ggsn->cfg.shutdown = true;
+ INIT_LLIST_HEAD(&ggsn->apn_list);
+
+ llist_add_tail(&ggsn->list, &g_ggsn_list);
+ return ggsn;
+}
+
+struct apn_ctx *ggsn_find_apn(struct ggsn_ctx *ggsn, const char *name)
+{
+ struct apn_ctx *apn;
+
+ llist_for_each_entry(apn, &ggsn->apn_list, list) {
+ if (!strcmp(apn->cfg.name, name))
+ return apn;
+ }
+ return NULL;
+}
+
+struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
+{
+ struct apn_ctx *apn = ggsn_find_apn(ggsn, name);
+ if (apn)
+ return apn;
+
+ apn = talloc_zero(ggsn, struct apn_ctx);
+ if (!apn)
+ return NULL;
+ apn->ggsn = ggsn;
+ apn->cfg.name = talloc_strdup(apn, name);
+ apn->cfg.shutdown = true;
+ INIT_LLIST_HEAD(&apn->cfg.name_list);
+
+ llist_add_tail(&apn->list, &ggsn->apn_list);
+ return apn;
+}
+
+/* GGSN Node */
+
+static struct cmd_node ggsn_node = {
+ GGSN_NODE,
+ "%s(config-ggsn)# ",
+ 1,
+};
+
+DEFUN(cfg_ggsn, cfg_ggsn_cmd,
+ "ggsn NAME",
+ "Configure the Gateway GPRS Support Node\n" "GGSN Name (has only local significance)\n")
+{
+ struct ggsn_ctx *ggsn;
+
+ ggsn = ggsn_find_or_create(tall_ggsn_ctx, argv[0]);
+ if (!ggsn)
+ return CMD_WARNING;
+
+ vty->node = GGSN_NODE;
+ vty->index = ggsn;
+ vty->index_sub = &ggsn->cfg.description;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_no_ggsn, cfg_no_ggsn_cmd,
+ "no ggsn NAME",
+ NO_STR "Remove the named Gateway GPRS Support Node\n"
+ "GGSN Name (has only local significance)\n")
+{
+ struct ggsn_ctx *ggsn;
+
+ ggsn = ggsn_find(argv[0]);
+ if (!ggsn) {
+ vty_out(vty, "%% No such GGSN '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!ggsn->cfg.shutdown) {
+ vty_out(vty, "%% GGSN %s is still active, please shutdown first%s",
+ ggsn->cfg.name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!llist_empty(&ggsn->apn_list)) {
+ vty_out(vty, "%% GGSN %s still has APNs configured, please remove first%s",
+ ggsn->cfg.name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_del(&ggsn->list);
+ talloc_free(ggsn);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_local_ip, cfg_ggsn_local_ip_cmd,
+ "gtp local-ip A.B.C.D",
+ "GTP Parameters\n"
+ "Set the IP address for the local GTP bind\n"
+ "IPv4 Address\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ size_t t;
+
+ ippool_aton(&ggsn->cfg.listen_addr, &t, argv[0], 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_state_dir, cfg_ggsn_state_dir_cmd,
+ "gtp state-dir PATH",
+ "GTP Parameters\n"
+ "Set the directory for the GTP State file\n"
+ "Local Directory\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+
+ osmo_talloc_replace_string(ggsn, &ggsn->cfg.state_dir, argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_apn, cfg_ggsn_apn_cmd,
+ "apn NAME", "APN Configuration\n" "APN Name\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ struct apn_ctx *apn;
+
+ apn = ggsn_find_or_create_apn(ggsn, argv[0]);
+ if (!apn)
+ return CMD_WARNING;
+
+ vty->node = APN_NODE;
+ vty->index = apn;
+ vty->index_sub = &ggsn->cfg.description;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_no_apn, cfg_ggsn_no_apn_cmd,
+ "no apn NAME",
+ NO_STR "Remove APN Configuration\n" "APN Name\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ struct apn_ctx *apn;
+
+ apn = ggsn_find_apn(ggsn, argv[0]);
+ if (!apn) {
+ vty_out(vty, "%% No such APN '%s'%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!apn->cfg.shutdown) {
+ vty_out(vty, "%% APN %s still active, please shutdown first%s",
+ apn->cfg.name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ llist_del(&apn->list);
+ talloc_free(apn);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_default_apn, cfg_ggsn_default_apn_cmd,
+ "default-apn NAME",
+ "Set a default-APN to be used if no other APN matches\n"
+ "APN Name\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ struct apn_ctx *apn;
+
+ apn = ggsn_find_apn(ggsn, argv[0]);
+ if (!apn) {
+ vty_out(vty, "%% No APN of name '%s' found%s", argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ ggsn->cfg.default_apn = apn;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_no_default_apn, cfg_ggsn_no_default_apn_cmd,
+ "no default-apn",
+ NO_STR "Remove default-APN to be used if no other APN matches\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+ ggsn->cfg.default_apn = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_shutdown, cfg_ggsn_shutdown_cmd,
+ "shutdown",
+ "Put the GGSN in administrative shut-down\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+
+ if (!ggsn->cfg.shutdown) {
+ ggsn_stop(ggsn);
+ ggsn->cfg.shutdown = true;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd,
+ "no shutdown",
+ NO_STR "Remove the GGSN from administrative shut-down\n")
+{
+ struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index;
+
+ if (ggsn->cfg.shutdown) {
+ if (ggsn_start(ggsn) < 0) {
+ vty_out(vty, "%% Failed to start GGSN, check log for details%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ ggsn->cfg.shutdown = false;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* APN Node */
+
+static struct cmd_node apn_node = {
+ APN_NODE,
+ "%s(config-ggsn-apn)# ",
+ 1,
+};
+
+static const struct value_string pdp_type_names[] = {
+ { APN_TYPE_IPv4, "v4" },
+ { APN_TYPE_IPv6, "v6" },
+ { APN_TYPE_IPv4v6, "v4v6" },
+ { 0, NULL }
+};
+
+static const struct value_string apn_gtpu_mode_names[] = {
+ { APN_GTPU_MODE_TUN, "tun" },
+ { APN_GTPU_MODE_KERNEL_GTP, "kernel-gtp" },
+ { 0, NULL }
+};
+
+
+#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
+ "IPv6(-only) PDP Type\n" \
+ "IPv4v6 (dual-stack) PDP Type\n"
+
+DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
+ "type-support (v4|v6|v4v6)",
+ "Enable support for PDP Type\n"
+ V4V6V46_STRING)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ uint32_t type = get_string_value(pdp_type_names, argv[0]);
+
+ apn->cfg.apn_type_mask |= type;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_type_support, cfg_apn_no_type_support_cmd,
+ "no type-support (v4|v6|v4v6)",
+ NO_STR "Disable support for PDP Type\n"
+ V4V6V46_STRING)
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ uint32_t type = get_string_value(pdp_type_names, argv[0]);
+
+ apn->cfg.apn_type_mask &= ~type;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_gtpu_mode, cfg_apn_gtpu_mode_cmd,
+ "gtpu-mode (tun|kernel-gtp)",
+ "Set the Mode for this APN (tun or Linux Kernel GTP)\n"
+ "GTP-U in userspace using TUN device\n"
+ "GTP-U in kernel using Linux Kernel GTP\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+
+ apn->cfg.gtpu_mode = get_string_value(apn_gtpu_mode_names, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
+ "tun-device NAME",
+ "Configure tun device name\n"
+ "TUN device name")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->tun.cfg.dev_name, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipup_script, cfg_apn_ipup_script_cmd,
+ "ipup-script PATH",
+ "Configure name/path of ip-up script\n"
+ "File/Path name of ip-up script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->tun.cfg.ipup_script, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_ipup_script, cfg_apn_no_ipup_script_cmd,
+ "no ipup-script",
+ NO_STR "Disable ip-up script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ talloc_free(apn->tun.cfg.ipup_script);
+ apn->tun.cfg.ipup_script = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipdown_script, cfg_apn_ipdown_script_cmd,
+ "ipdown-script PATH",
+ "Configure name/path of ip-down script\n"
+ "File/Path name of ip-down script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ osmo_talloc_replace_string(apn, &apn->tun.cfg.ipdown_script, argv[0]);
+ return CMD_SUCCESS;
+}
+
+/* convert prefix from "A.B.C.D/M" notation to in46_prefix */
+static void str2prefix(struct in46_prefix *pfx, const char *in)
+{
+ size_t t;
+
+ ippool_aton(&pfx->addr, &t, in, 0);
+ pfx->prefixlen = t;
+}
+
+DEFUN(cfg_apn_no_ipdown_script, cfg_apn_no_ipdown_script_cmd,
+ "no ipdown-script",
+ NO_STR "Disable ip-down script\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ talloc_free(apn->tun.cfg.ipdown_script);
+ apn->tun.cfg.ipdown_script = NULL;
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ip_prefix, cfg_apn_ip_prefix_cmd,
+ "ip prefix (static|dynamic) A.B.C.D/M",
+ IP_STR PREFIX_STR "IPv4 Adress/Prefix-Length\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ struct in46_prefix *pfx;
+
+ /* first update our parsed prefix */
+ if (!strcmp(argv[0], "static"))
+ pfx = &apn->v4.cfg.static_prefix;
+ else
+ pfx = &apn->v4.cfg.dynamic_prefix;
+ str2prefix(pfx, argv[1]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
+ "ipv6 prefix (static|dynamic) X:X::X:X/M",
+ IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ struct in46_prefix *pfx;
+
+ if (!strcmp(argv[0], "static"))
+ pfx = &apn->v6.cfg.static_prefix;
+ else
+ pfx = &apn->v6.cfg.dynamic_prefix;
+ str2prefix(pfx, argv[1]);
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(cfg_apn_dns, cfg_apn_dns_cmd,
+ "(ip|ipv6) dns <0-1> A.B.C.D",
+ IP_STR IP6_STR "Configure DNS Server\n" "primary/secondary DNS\n"
+ "IP address of DNS Sever\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ struct in46_addr *a;
+ int idx = atoi(argv[1]);
+ size_t dummy;
+
+ if (!strcmp(argv[0], "ip"))
+ a = &apn->v4.cfg.dns[idx];
+ else
+ a = &apn->v6.cfg.dns[idx];
+
+ ippool_aton(a, &dummy, argv[2], 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_dns, cfg_apn_no_dns_cmd,
+ "no (ip|ipv6) dns <0-1>",
+ NO_STR IP_STR IP6_STR "Disable DNS Server\n" "primary/secondary DNS\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+ struct in46_addr *a;
+ int idx = atoi(argv[1]);
+
+ if (!strcmp(argv[0], "ip"))
+ a = &apn->v4.cfg.dns[idx];
+ else
+ a = &apn->v6.cfg.dns[idx];
+
+ memset(a, 0, sizeof(*a));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
+ "shutdown",
+ "Put the APN in administrative shut-down\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+
+ if (!apn->cfg.shutdown) {
+ apn_stop(apn, false);
+ apn->cfg.shutdown = true;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
+ "no shutdown",
+ NO_STR "Remove the APN from administrative shut-down\n")
+{
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+
+ if (apn->cfg.shutdown) {
+ if (apn_start(apn) < 0) {
+ vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ apn->cfg.shutdown = false;
+ }
+
+ return CMD_SUCCESS;
+}
+
+
+static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_prefix *pfx)
+{
+ vty_out(vty, "%s %s/%u%s", pre, in46a_ntoa(&pfx->addr), pfx->prefixlen, VTY_NEWLINE);
+}
+
+static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
+{
+ unsigned int i;
+
+ vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
+ if (apn->cfg.description)
+ vty_out(vty, " description %s%s", apn->cfg.description, VTY_NEWLINE);
+ vty_out(vty, " gtpu-mode %s%s", get_value_string(apn_gtpu_mode_names, apn->cfg.gtpu_mode),
+ VTY_NEWLINE);
+ if (apn->tun.cfg.dev_name)
+ vty_out(vty, " tun-device %s%s", apn->tun.cfg.dev_name, VTY_NEWLINE);
+ if (apn->tun.cfg.do_ifconfig)
+ vty_out(vty, " do-ifconfig%s", VTY_NEWLINE);
+ if (apn->tun.cfg.ipup_script)
+ vty_out(vty, " ipup-script %s%s", apn->tun.cfg.ipup_script, VTY_NEWLINE);
+ if (apn->tun.cfg.ipdown_script)
+ vty_out(vty, " ipdown-script %s%s", apn->tun.cfg.ipdown_script, VTY_NEWLINE);
+
+ for (i = 0; i < 32; i++) {
+ if (!(apn->cfg.apn_type_mask & (1 << i)))
+ continue;
+ vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (1 << i)),
+ VTY_NEWLINE);
+ }
+
+ /* IPv4 prefixes + DNS */
+ if (apn->v4.cfg.static_prefix.addr.len)
+ vty_dump_prefix(vty, " ip prefix static", &apn->v4.cfg.static_prefix);
+ if (apn->v4.cfg.dynamic_prefix.addr.len)
+ vty_dump_prefix(vty, " ip prefix dynamic", &apn->v4.cfg.dynamic_prefix);
+ for (i = 0; i < ARRAY_SIZE(apn->v4.cfg.dns); i++) {
+ if (!apn->v4.cfg.dns[i].len)
+ continue;
+ vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v4.cfg.dns[i]), VTY_NEWLINE);
+ }
+
+ /* IPv6 prefixes + DNS */
+ if (apn->v6.cfg.static_prefix.addr.len)
+ vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
+ if (apn->v6.cfg.dynamic_prefix.addr.len)
+ vty_dump_prefix(vty, " ipv6 prefix dynamic", &apn->v6.cfg.dynamic_prefix);
+ for (i = 0; i < ARRAY_SIZE(apn->v6.cfg.dns); i++) {
+ if (!apn->v6.cfg.dns[i].len)
+ continue;
+ vty_out(vty, " ip dns %u %s%s", i, in46a_ntoa(&apn->v6.cfg.dns[i]), VTY_NEWLINE);
+ }
+
+ /* must be last */
+ vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
+}
+
+static int config_write_ggsn(struct vty *vty)
+{
+ struct ggsn_ctx *ggsn;
+
+ llist_for_each_entry(ggsn, &g_ggsn_list, list) {
+ struct apn_ctx *apn;
+ vty_out(vty, "ggsn %s%s", ggsn->cfg.name, VTY_NEWLINE);
+ if (ggsn->cfg.description)
+ vty_out(vty, " description %s%s", ggsn->cfg.description, VTY_NEWLINE);
+ vty_out(vty, " gtp state-dir %s%s", ggsn->cfg.state_dir, VTY_NEWLINE);
+ vty_out(vty, " gtp local-ip %s%s", in46a_ntoa(&ggsn->cfg.listen_addr), VTY_NEWLINE);
+ llist_for_each_entry(apn, &ggsn->apn_list, list)
+ config_write_apn(vty, apn);
+ if (ggsn->cfg.default_apn)
+ vty_out(vty, " default-apn %s%s", ggsn->cfg.default_apn->cfg.name, VTY_NEWLINE);
+ /* must be last */
+ vty_out(vty, " %sshutdown%s", ggsn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
+ }
+
+ return 0;
+}
+
+static const char *print_gsnaddr(const struct ul16_t *in)
+{
+ struct in46_addr in46;
+
+ in46.len = in->l;
+ OSMO_ASSERT(in->l <= sizeof(in46.v6));
+ memcpy(&in46.v6, in->v, in->l);
+
+ return in46a_ntoa(&in46);
+}
+
+static void show_one_pdp(struct vty *vty, struct pdp_t *pdp)
+{
+ struct in46_addr eua46;
+
+ vty_out(vty, "IMSI: %"PRIu64", NSAPI: %u, MSISDN: %s%s", pdp->imsi, pdp->nsapi,
+ osmo_hexdump_nospc(pdp->msisdn.v, pdp->msisdn.l), VTY_NEWLINE);
+
+ vty_out(vty, " Control: %s:%04x ", print_gsnaddr(&pdp->gsnlc), pdp->teic_own);
+ vty_out(vty, "<-> %s:%04x%s", print_gsnaddr(&pdp->gsnlu), pdp->teid_own, VTY_NEWLINE);
+
+ vty_out(vty, " Data: %s:%04x ", print_gsnaddr(&pdp->gsnrc), pdp->teic_gn);
+ vty_out(vty, "<-> %s:%04x%s", print_gsnaddr(&pdp->gsnru), pdp->teid_gn, VTY_NEWLINE);
+
+ in46a_from_eua(&pdp->eua, &eua46);
+ vty_out(vty, " End-User Address: %s\n", in46a_ntoa(&eua46));
+}
+
+DEFUN(show_pdpctx_imsi, show_pdpctx_imsi_cmd,
+ "show pdp-context imsi IMSI [<0-15>]",
+ SHOW_STR "Display information on PDP Context\n"
+ "PDP contexts for given IMSI\n"
+ "PDP context for given NSAPI\n")
+{
+ uint64_t imsi = strtoull(argv[0], NULL, 10);
+ unsigned int nsapi;
+ struct pdp_t *pdp;
+ int num_found = 0;
+
+ if (argc > 1) {
+ nsapi = atoi(argv[1]);
+ if (pdp_getimsi(&pdp, imsi, nsapi)) {
+ show_one_pdp(vty, pdp);
+ num_found++;
+ }
+ } else {
+ for (nsapi = 0; nsapi < PDP_MAXNSAPI; nsapi++) {
+ if (!pdp_getimsi(&pdp, imsi, nsapi))
+ continue;
+ show_one_pdp(vty, pdp);
+ num_found++;
+ }
+ }
+ if (num_found == 0) {
+ vty_out(vty, "%% No such PDP context found%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ } else
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_apn_all, show_apn_all_cmd,
+ "show apn all",
+ SHOW_STR "Display information on APN\n" "Show all APNs\n")
+{
+ return CMD_SUCCESS;
+}
+
+static void show_apn(struct vty *vty, struct apn_ctx *apn)
+{
+ vty_out(vty, " APN: %s%s", apn->cfg.name, VTY_NEWLINE);
+ /* FIXME */
+}
+
+static void show_one_ggsn(struct vty *vty, struct ggsn_ctx *ggsn)
+{
+ struct apn_ctx *apn;
+ vty_out(vty, "GGSN %s: Bound to %s%s\n", ggsn->cfg.name, in46a_ntoa(&ggsn->cfg.listen_addr),
+ VTY_NEWLINE);
+ /* FIXME */
+
+ llist_for_each_entry(apn, &ggsn->apn_list, list)
+ show_apn(vty, apn);
+}
+
+DEFUN(show_ggsn, show_ggsn_cmd,
+ "show ggsn [NAME]",
+ SHOW_STR "Display information on the GGSN\n")
+{
+ struct ggsn_ctx *ggsn;
+
+ if (argc == 0) {
+ llist_for_each_entry(ggsn, &g_ggsn_list, list)
+ show_one_ggsn(vty, ggsn);
+ } else {
+ ggsn = ggsn_find(argv[0]);
+ if (!ggsn)
+ return CMD_WARNING;
+ show_one_ggsn(vty, ggsn);
+ }
+
+ return CMD_SUCCESS;
+}
+
+int ggsn_vty_init(void)
+{
+ install_element_ve(&show_pdpctx_imsi_cmd);
+ install_element_ve(&show_apn_all_cmd);
+ install_element_ve(&show_ggsn_cmd);
+
+ install_element(CONFIG_NODE, &cfg_ggsn_cmd);
+ install_element(CONFIG_NODE, &cfg_no_ggsn_cmd);
+ install_node(&ggsn_node, config_write_ggsn);
+ vty_install_default(GGSN_NODE);
+ install_element(GGSN_NODE, &cfg_description_cmd);
+ install_element(GGSN_NODE, &cfg_no_description_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_shutdown_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_no_shutdown_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_local_ip_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_state_dir_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_apn_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_no_apn_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_default_apn_cmd);
+ install_element(GGSN_NODE, &cfg_ggsn_no_default_apn_cmd);
+
+ install_node(&apn_node, NULL);
+ vty_install_default(APN_NODE);
+ install_element(APN_NODE, &cfg_description_cmd);
+ install_element(APN_NODE, &cfg_no_description_cmd);
+ install_element(APN_NODE, &cfg_apn_shutdown_cmd);
+ install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
+ install_element(APN_NODE, &cfg_apn_gtpu_mode_cmd);
+ install_element(APN_NODE, &cfg_apn_type_support_cmd);
+ install_element(APN_NODE, &cfg_apn_no_type_support_cmd);
+ install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
+ install_element(APN_NODE, &cfg_apn_ipup_script_cmd);
+ install_element(APN_NODE, &cfg_apn_no_ipup_script_cmd);
+ install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
+ install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
+ install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
+ install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
+ install_element(APN_NODE, &cfg_apn_dns_cmd);
+ install_element(APN_NODE, &cfg_apn_no_dns_cmd);
+
+ return 0;
+}
+
+static int ggsn_vty_is_config_node(struct vty *vty, int node)
+{
+ switch (node) {
+ case GGSN_NODE:
+ case APN_NODE:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int ggsn_vty_go_parent(struct vty *vty)
+{
+ switch (vty->node) {
+ case GGSN_NODE:
+ vty->node = CONFIG_NODE;
+ vty->index = NULL;
+ vty->index_sub = NULL;
+ break;
+ case APN_NODE:
+ vty->node = GGSN_NODE;
+ {
+ struct apn_ctx *apn = vty->index;
+ vty->index = apn->ggsn;
+ vty->index_sub = &apn->ggsn->cfg.description;
+ }
+ break;
+ }
+
+ return vty->node;
+}
+
+static const char ggsn_copyright[] =
+ "Copyright (C) 2011-2017 Harald Welte <laforge@gnumonks.org>\r\n"
+ "Copyright (C) 2012-2017 Holger Hans Peter Freyther <holger@moiji-mobile.com>\r\n"
+ "Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH\r\n"
+ "Copyright (C) 2002-2005 Mondru AB\r\n"
+ "License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl-2.0.html>\r\n"
+ "This is free software: you are free to change and redistribute it.\r\n"
+ "There is NO WARRANTY, to the extent permitted by law.\r\n";
+
+struct vty_app_info g_vty_info = {
+ .name = "OpenGGSN",
+ .version = PACKAGE_VERSION,
+ .copyright = ggsn_copyright,
+ .go_parent_cb = ggsn_vty_go_parent,
+ .is_config_node = ggsn_vty_is_config_node,
+};