From a22941aef3242ebbc011ccfab25c9f8ffecbc27b Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 12 Aug 2017 15:07:02 +0200 Subject: Introduce a VTY, factually turning OpenGGSN into an Osmocom program Change-Id: I9613ca3436e77ea132c47f0096df7c5050d7e826 --- ggsn/Makefile.am | 10 +- ggsn/cmdline.c | 1162 ----------------------------------------------------- ggsn/cmdline.h | 275 ------------- ggsn/ggsn.c | 935 +++++++++++++++++++++--------------------- ggsn/ggsn.h | 133 ++++++ ggsn/ggsn_vty.c | 846 ++++++++++++++++++++++++++++++++++++++ lib/tun.c | 4 +- lib/tun.h | 2 +- sgsnemu/sgsnemu.c | 2 +- 9 files changed, 1441 insertions(+), 1928 deletions(-) delete mode 100644 ggsn/cmdline.c delete mode 100644 ggsn/cmdline.h create mode 100644 ggsn/ggsn.h create mode 100644 ggsn/ggsn_vty.c 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 -#include -#include - -#ifndef FIX_UNUSED -#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ -#endif - -#include - -#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, ¶ms, 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, ¶ms); -} - -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 /* 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 f325506..387048d 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 * * 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 - #ifdef HAVE_STDINT_H #include #endif #include -#include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include -#include -#include - #include +#include +#include -#include +#include +#include +#include +#include #include +#include +#include +#include #include #include +#include #include +#include +#include +#include +#include +#include #include "../lib/tun.h" #include "../lib/ippool.h" @@ -62,82 +60,165 @@ #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(level, apn, fmt, args...) \ + LOGP(DGGSN, 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; -} +#define LOGPGGSN(level, ggsn, fmt, args...) \ + LOGP(DGGSN, level, "GGSN(%s): " fmt, (ggsn)->cfg.name, ## args) + +#define LOGPPDP(level, pdp, fmt, args...) \ + LOGP(DGGSN, level, "PDP(%"PRIu64":%u): " fmt, (pdp)->imsi, (pdp)->nsapi, ## args) -/* Used to write process ID to file. Assume someone else will delete */ -void log_pid(char *pidfile) +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); + + +int apn_stop(struct apn_ctx *apn, bool force) { - FILE *file; - mode_t oldmask; - - 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->started) + return 0; + + LOGPAPN(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( 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(LOGL_INFO, apn, "Closing TUN device\n"); + osmo_fd_unregister(&apn->tun.fd); + tun_free(apn->tun.tun); + apn->tun.tun = NULL; } - fprintf(file, "%d\n", (int)getpid()); - fclose(file); + + if (apn->v4.pool) { + LOGPAPN(LOGL_INFO, apn, "Releasing IPv4 pool\n"); + ippool_free(apn->v4.pool); + apn->v4.pool = NULL; + } + if (apn->v6.pool) { + LOGPAPN(LOGL_INFO, apn, "Releasing IPv6 pool\n"); + ippool_free(apn->v6.pool); + apn->v6.pool = NULL; + } + + apn->started = false; + 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; + if (apn->started) + return 0; - switch (fork()) { - case -1: - return (-1); - case 0: + LOGPAPN(LOGL_INFO, apn, "Starting\n"); + switch (apn->cfg.gtpu_mode) { + case APN_GTPU_MODE_TUN: + LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name); + if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name)) { + LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n"); + return -1; + } + LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", apn->tun.tun->devname); + + /* 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->v4.cfg.ifconfig_prefix.addr.len) { + LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n", + in46p_ntoa(&apn->v4.cfg.ifconfig_prefix)); + if (tun_setaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL, + apn->v4.cfg.ifconfig_prefix.prefixlen)) { + LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n", + in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno)); + apn_stop(apn, false); + return -1; + } + } + + if (apn->v6.cfg.ifconfig_prefix.addr.len) { + LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n", + in46p_ntoa(&apn->v6.cfg.ifconfig_prefix)); + if (tun_setaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL, + apn->v6.cfg.ifconfig_prefix.prefixlen)) { + LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s\n", + in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno)); + apn_stop(apn, false); + return -1; + } + } + + if (apn->tun.cfg.ipup_script) { + LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n", + apn->tun.cfg.ipup_script); + tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script); + } + /* set back-pointer from TUN device to APN */ + apn->tun.tun->priv = apn; + break; + case APN_GTPU_MODE_KERNEL_GTP: + LOGPAPN(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(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.dynamic_prefix.addr.len) { + LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n", + in46p_ntoa(&apn->v4.cfg.dynamic_prefix)); + if (ippool_new(&apn->v4.pool, &apn->v4.cfg.dynamic_prefix, + &apn->v4.cfg.static_prefix, 0)) { + LOGPAPN(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.dynamic_prefix.addr.len) { + LOGPAPN(LOGL_INFO, apn, "Creating IPv6 pool %s\n", + in46p_ntoa(&apn->v6.cfg.dynamic_prefix)); + if (ippool_new(&apn->v6.pool, &apn->v6.cfg.dynamic_prefix, + &apn->v6.cfg.static_prefix, 0)) { + LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv6 pool\n"); + apn_stop(apn, false); + return -1; + } } - return (0); + + LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n"); + apn->started = true; + 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,27 +229,29 @@ 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) { - LOGP(DGGSN, LOGL_ERROR, "Failed to create and send TRAP for IMSI %" PRIu64 " [%s].\n", pdp->imsi, var); + if (ctrl_cmd_send_trap(g_ctrlh, var, val) < 0) { + LOGPPDP(LOGL_ERROR, pdp, "Failed to create and send TRAP %s\n", var); return false; } return true; } -int delete_context(struct pdp_t *pdp) +static int delete_context(struct pdp_t *pdp) { - DEBUGP(DGGSN, "Deleting PDP context\n"); + struct gsn_t *gsn = pdp->gsn; + struct ippoolm_t *ipp = (struct ippoolm_t *)pdp->peer; + + LOGPPDP(LOGL_INFO, pdp, "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!"); + LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n"); if (gtp_kernel_tunnel_del(pdp)) { - SYS_ERR(DGGSN, LOGL_ERROR, 0, - "Cannot delete tunnel from kernel: %s\n", + LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n", strerror(errno)); } @@ -236,31 +319,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 +400,29 @@ 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); + + LOGPPDP(LOGL_DEBUG, pdp, "Processing 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 */ + LOGPPDP(LOGL_NOTICE, pdp, "Unknown APN '%s', rejecting\n", name_buf); + 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) @@ -290,21 +434,30 @@ int create_context_ind(struct pdp_t *pdp) pdp->qos_neg.l = pdp->qos_req.l; if (in46a_from_eua(&pdp->eua, &addr)) { - SYS_ERR(DGGSN, LOGL_ERROR, 0, "Cannot decode EUA from MS/SGSN: %s", + LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n", osmo_hexdump(pdp->eua.v, pdp->eua.l)); gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_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) { + LOGPPDP(LOGL_ERROR, pdp, "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 +467,46 @@ 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); + LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: TEIC=%u IP=%s\n", + pdp->teic_own, in46a_ntoa(&member->addr)); gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ); return 0; /* Success */ + +err_pool_full: + LOGPPDP(LOGL_ERROR, pdp, "Cannot allocate IP address from pool (full!)\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,20 +514,25 @@ 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"); + LOGP(DTUN, LOGL_NOTICE, "non-IPv packet received from tun\n"); return -1; } - DEBUGP(DGGSN, "Received packet from tun!\n"); + /* IPv6 packet but no IPv6 pool, or IPv4 packet with no IPv4 pool */ + if (!pool) + return 0; + + DEBUGP(DTUN, "Received packet from tun!\n"); - if (ippool_getip(ippool, &ipm, &dst)) { - DEBUGP(DGGSN, "Received packet with no destination!!!\n"); + if (ippool_getip(pool, &ipm, &dst)) { + DEBUGP(DTUN, "Received packet with no PDP contex!!\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,435 +541,243 @@ 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; - DEBUGP(DGGSN, "encaps_tun. Packet received: forwarding to tun\n"); + LOGPPDP(LOGL_DEBUG, pdp, "Packet received: forwarding to tun\n"); switch (iph->version) { 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; default: - LOGP(DGGSN, LOGL_ERROR, "Packet from MS is neither IPv4 nor IPv6\n"); + LOGPPDP(LOGL_ERROR, pdp, "Packet from MS is neither IPv4 nor IPv6: %s\n", + osmo_hexdump(pack, len)); return -1; } return tun_encaps((struct tun_t *)pdp->ipif, pack, len); } -int main(int argc, char **argv) -{ - /* gengeopt declarations */ - struct gengetopt_args_info args_info; +static char *config_file = "openggsn.cfg"; - struct hostent *host; +/* callback for tun device osmocom select loop integration */ +static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what) +{ + struct apn_ctx *apn = fd->data; - /* 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"); + OSMO_ASSERT(what & BSC_FD_READ); - 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: + OSMO_ASSERT(0); + 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) +{ + LOGP(DGGSN, LOGL_NOTICE, "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 */ - struct in46_prefix i46p; - size_t prefixlen; - if (!args_info.dynip_arg) { - if (ippool_aton(&i46p.addr, &prefixlen, args_info.net_arg, 0)) { - SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to parse dynamic pool"); - exit(1); - } - } else { - if (ippool_aton(&i46p.addr, &prefixlen, args_info.dynip_arg, 0)) { - SYS_ERR(DIP, LOGL_ERROR, 0, "Failed to parse dynamic pool"); - exit(1); - } - } - i46p.prefixlen = prefixlen; - if (ippool_new(&ippool, &i46p, NULL, 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) +{ + struct apn_ctx *apn; + 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); - } - } + if (ggsn->started) + return 0; - 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; + LOGPGGSN(LOGL_INFO, ggsn, "Starting GGSN\n"); + + /* Start libgtp listener */ + if (gtp_new(&ggsn->gsn, ggsn->cfg.state_dir, &ggsn->cfg.listen_addr.v4, GTP_MODE_GGSN)) { + LOGPGGSN(LOGL_ERROR, ggsn, "Failed to create GTP: %s\n", strerror(errno)); + return -1; } - pco.l = cur; - /* patch in length values */ - pco.v[3] = pco.l - 4; - pco.v[7] = pco.l - 4; + ggsn->gsn->priv = ggsn; - /* ipup */ - ipup = args_info.ipup_arg; + /* 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); - /* ipdown */ - ipdown = args_info.ipdown_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); - /* Timelimit */ - timelimit = args_info.timelimit_arg; - starttime = time(NULL); + 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); - /* 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; + /* Start GTP re-transmission timer */ + osmo_timer_setup(&ggsn->gtp_timer, ggsn_gtp_tmr_cb, ggsn); - /* 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); - } - } + 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); - /* pidfile */ - /* This has to be done after we have our final pid */ - if (args_info.pidfile_arg) { - log_pid(args_info.pidfile_arg); - } + LOGPGGSN(LOGL_NOTICE, ggsn, "Successfully started\n"); + ggsn->started = true; - DEBUGP(DGGSN, "gtpclient: Initialising GTP tunnel\n"); + llist_for_each_entry(apn, &ggsn->apn_list, list) + apn_start(apn); - 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 (args_info.gtp_linux_given) { - if (gtp_kernel_init(gsn, &net.v4, prefixlen, args_info.net_arg) < 0) { - SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to initialize kernel GTP\n"); - goto err; - } - } + return 0; +} - 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); +/* Stop a given GGSN */ +int ggsn_stop(struct ggsn_ctx *ggsn) +{ + struct apn_ctx *apn; - 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); - } + if (!ggsn->started) + return 0; - /* skip the configuration of the tun0 if we're using the gtp0 device */ - if (gtp_kernel_enabled()) - goto skip_tun; + /* iterate over all APNs and stop them */ + llist_for_each_entry(apn, &ggsn->apn_list, list) + apn_stop(apn, true); - /* 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); - } + osmo_timer_del(&ggsn->gtp_timer); - 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); + osmo_fd_unregister(&ggsn->gtp_fd1u); + osmo_fd_unregister(&ggsn->gtp_fd1c); + osmo_fd_unregister(&ggsn->gtp_fd0); + + if (ggsn->gsn) { + gtp_free(ggsn->gsn); + ggsn->gsn = NULL; } - tun_set_cb_ind(tun, cb_tun_ind); - if (tun->fd > maxfd) - maxfd = tun->fd; + ggsn->started = false; + return 0; +} - if (ipup) - tun_runscript(tun, ipup); +static void handle_options(int argc, char **argv) +{ + /* FIXME */ +} -skip_tun: +int main(int argc, char **argv) +{ + struct ggsn_ctx *ggsn; + int rc; - /******************************************************************/ - /* Main select loop */ - /******************************************************************/ + /* Handle keyboard interrupt SIGINT */ + tall_ggsn_ctx = talloc_named_const(NULL, 0, "openggsn"); + msgb_talloc_ctx_init(tall_ggsn_ctx, 0); - while ((((starttime + timelimit) > time(NULL)) || (0 == timelimit)) - && (!end)) { + signal(SIGINT, &signal_handler); + signal(SIGABRT, &signal_handler); + signal(SIGUSR1, &signal_handler); + signal(SIGUSR2, &signal_handler); - 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); + osmo_init_ignore_signals(); + osmo_init_logging(&log_info); + osmo_stats_init(tall_ggsn_ctx); - 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; - } + 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); - 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); - } + handle_options(argc, argv); - if (FD_ISSET(gsn->fd0, &fds)) - gtp_decaps0(gsn); + rate_ctr_init(tall_ggsn_ctx); - if (FD_ISSET(gsn->fd1c, &fds)) - gtp_decaps1c(gsn); + rc = vty_read_config_file(config_file, NULL); + if (rc < 0) { + fprintf(stderr, "Failed to open config file: '%s'\n", config_file); + exit(2); + } - if (FD_ISSET(gsn->fd1u, &fds)) - gtp_decaps1u(gsn); + rc = telnet_init_dynif(tall_ggsn_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_GGSN); + if (rc < 0) + exit(1); - osmo_select_main(1); + 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); } -err: - gtp_kernel_stop(); - cmdline_parser_free(&args_info); - ippool_free(ippool); - gtp_free(gsn); - if (tun) - tun_free(tun); - return 1; + if (daemonize) { + rc = osmo_daemonize(); + if (rc < 0) { + perror("Error during daemonize"); + exit(1); + } + } +#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 + + /* Main select loop */ + while (!end) { + osmo_select_main(0); + } + + 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..bab6cf7 --- /dev/null +++ b/ggsn/ggsn.h @@ -0,0 +1,133 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#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 ifconfig_prefix; + 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; + + bool started; + + 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; + /* 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; + + bool started; + + 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..4f05bd7 --- /dev/null +++ b/ggsn/ggsn_vty.c @@ -0,0 +1,846 @@ +/* + * (C) 2017 by Harald Welte + * 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 . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "../gtp/gtp.h" +#include "../gtp/pdp.h" + +#include "ggsn.h" + +#define PREFIX_STR "Prefix (Network/Netmask)\n" +#define IFCONFIG_STR "GGSN-based interface configuration\n" +#define GGSN_STR "Gateway GPRS Support NODE (GGSN)\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 ggsn", + "Put the GGSN in administrative shut-down\n" GGSN_STR) +{ + struct ggsn_ctx *ggsn = (struct ggsn_ctx *) vty->index; + + if (!ggsn->cfg.shutdown) { + if (ggsn_stop(ggsn)) { + vty_out(vty, "%% Failed to shutdown GGSN%s", VTY_NEWLINE); + return CMD_WARNING; + } + ggsn->cfg.shutdown = true; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_ggsn_no_shutdown, cfg_ggsn_no_shutdown_cmd, + "no shutdown ggsn", + NO_STR GGSN_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_ip_ifconfig, cfg_apn_ip_ifconfig_cmd, + "ip ifconfig A.B.C.D/M", + IP_STR IFCONFIG_STR "IPv4 Adress/Prefix-Length\n") +{ + struct apn_ctx *apn = (struct apn_ctx *) vty->index; + str2prefix(&apn->v4.cfg.ifconfig_prefix, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd, + "no ip ifconfig", + NO_STR IP_STR IFCONFIG_STR) +{ + struct apn_ctx *apn = (struct apn_ctx *) vty->index; + memset(&apn->v4.cfg.ifconfig_prefix, 0, sizeof(apn->v4.cfg.ifconfig_prefix)); + 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_ipv6_ifconfig, cfg_apn_ipv6_ifconfig_cmd, + "ipv6 ifconfig X:X::X:X/M", + IP6_STR IFCONFIG_STR "IPv6 Adress/Prefix-Length\n") +{ + struct apn_ctx *apn = (struct apn_ctx *) vty->index; + str2prefix(&apn->v6.cfg.ifconfig_prefix, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_no_ipv6_ifconfig, cfg_apn_no_ipv6_ifconfig_cmd, + "no ipv6 ifconfig", + NO_STR IP6_STR IFCONFIG_STR) +{ + struct apn_ctx *apn = (struct apn_ctx *) vty->index; + memset(&apn->v6.cfg.ifconfig_prefix, 0, sizeof(apn->v6.cfg.ifconfig_prefix)); + return CMD_SUCCESS; +} + +#define DNS_STRINGS "Configure DNS Server\n" "primary/secondary DNS\n" "IP address of DNS Sever\n" + +DEFUN(cfg_apn_ip_dns, cfg_apn_ip_dns_cmd, + "ip dns <0-1> A.B.C.D", + IP_STR DNS_STRINGS) +{ + struct apn_ctx *apn = (struct apn_ctx *) vty->index; + int idx = atoi(argv[0]); + size_t dummy; + + ippool_aton(&apn->v4.cfg.dns[idx], &dummy, argv[1], 0); + + return CMD_SUCCESS; +} + +DEFUN(cfg_apn_ipv6_dns, cfg_apn_ipv6_dns_cmd, + "ipv6 dns <0-1> X:X::X:X", + IP6_STR DNS_STRINGS) +{ + struct apn_ctx *apn = (struct apn_ctx *) vty->index; + int idx = atoi(argv[0]); + size_t dummy; + + ippool_aton(&apn->v6.cfg.dns[idx], &dummy, argv[1], 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) { + if (apn_stop(apn, false)) { + vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE); + return CMD_WARNING; + } + 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%s", pre, in46p_ntoa(pfx), 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.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); + } + if (apn->v4.cfg.ifconfig_prefix.addr.len) + vty_dump_prefix(vty, " ip ifconfig ", &apn->v4.cfg.ifconfig_prefix); + + /* 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); + } + if (apn->v6.cfg.ifconfig_prefix.addr.len) + vty_dump_prefix(vty, " ipv6 ifconfig ", &apn->v6.cfg.ifconfig_prefix); + + /* 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 ggsn%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_ip_dns_cmd); + install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd); + install_element(APN_NODE, &cfg_apn_no_dns_cmd); + install_element(APN_NODE, &cfg_apn_ip_ifconfig_cmd); + install_element(APN_NODE, &cfg_apn_no_ip_ifconfig_cmd); + install_element(APN_NODE, &cfg_apn_ipv6_ifconfig_cmd); + install_element(APN_NODE, &cfg_apn_no_ipv6_ifconfig_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 \r\n" + "Copyright (C) 2012-2017 Holger Hans Peter Freyther \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 \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, +}; diff --git a/lib/tun.c b/lib/tun.c index 6ca9be7..bf95630 100644 --- a/lib/tun.c +++ b/lib/tun.c @@ -713,7 +713,7 @@ int tun_delroute(struct tun_t *this, return tun_route(this, dst, gateway, mask, 1); } -int tun_new(struct tun_t **tun) +int tun_new(struct tun_t **tun, const char *dev_name) { #if defined(__linux__) @@ -754,6 +754,8 @@ int tun_new(struct tun_t **tun) /* Set device flags. For some weird reason this is also the method used to obtain the network interface name */ memset(&ifr, 0, sizeof(ifr)); + if (dev_name) + strcpy(ifr.ifr_name, dev_name); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Tun device, no packet info */ if (ioctl((*tun)->fd, TUNSETIFF, (void *)&ifr) < 0) { SYS_ERR(DTUN, LOGL_ERROR, errno, "ioctl() failed"); diff --git a/lib/tun.h b/lib/tun.h index 67ef39d..50ac806 100644 --- a/lib/tun.h +++ b/lib/tun.h @@ -65,7 +65,7 @@ struct tun_t { void *priv; }; -extern int tun_new(struct tun_t **tun); +extern int tun_new(struct tun_t **tun, const char *dev_name); extern int tun_free(struct tun_t *tun); extern int tun_decaps(struct tun_t *this); extern int tun_encaps(struct tun_t *tun, void *pack, unsigned len); diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c index 8c9cfd3..232706c 100644 --- a/sgsnemu/sgsnemu.c +++ b/sgsnemu/sgsnemu.c @@ -1466,7 +1466,7 @@ int main(int argc, char **argv) if (options.createif) { printf("Setting up interface\n"); /* Create a tunnel interface */ - if (tun_new((struct tun_t **)&tun)) { + if (tun_new((struct tun_t **)&tun, NULL)) { SYS_ERR(DSGSN, LOGL_ERROR, 0, "Failed to create tun"); exit(1); -- cgit v1.2.3