This series imports Pablo Neira Ayuso original gtp-kernel support patch unmodified to master and applies some fixes ontop of it.
The last patch in the series then add network namespace support to it. This state is in sync with gtp-kernel patch I posted a few minutes ago.
Andreas
--
Andreas Schultz (3): ggsn: update gpt-kernel logging to libosmocore ggsn: fix autotool pkg-config invokation ggsn: add network namespace support
Pablo Neira Ayuso (1): ggsn: add support for GTP kernel data encapsulation
configure.ac | 15 ++++ ggsn/Makefile.am | 11 ++- ggsn/cmdline.c | 48 +++++++++- ggsn/cmdline.ggo | 4 + ggsn/cmdline.h | 8 ++ ggsn/ggsn.c | 52 ++++++++++- ggsn/gtp-kernel.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ggsn/gtp-kernel.h | 53 +++++++++++ lib/Makefile.am | 4 +- lib/netns.c | 135 ++++++++++++++++++++++++++++ lib/netns.h | 26 ++++++ 11 files changed, 607 insertions(+), 7 deletions(-) create mode 100644 ggsn/gtp-kernel.c create mode 100644 ggsn/gtp-kernel.h create mode 100644 lib/netns.c create mode 100644 lib/netns.h
From: Pablo Neira Ayuso pablo@gnumonks.org
This patch adds the -g, --gtpnl=device option that allows you to enable the GTP kernel tunneling mode in openggsn. You have to specify the real downlink device that will be used to tunnel traffic, eg.
-g=eth0
This means that the gtp0 device will be created and it will use eth0 as the real device to encapsulate packet coming from the Internet that are addressed to the MS (so the tunnel devuce encapsulates these IP packets in GTP packets when traveling to the SGSN).
Alternatively, you can also add this to the ggsn.conf configuration file:
gtpnl eth0
The device has to be the real device that can route packets to the SGSN, if you select the wrong device, the kernel routing code may not find a way to reach the SSGN, you've been warned.
Therefore, if this option is set, the operational becomes the following:
1) A gtp0 device is created via rtnetlink and configure the socket encapsulation infrastructure in the kernel. 2) Whenever a PDP context is created, this adds the necessary tunnel configuration via genetlink GTP interface. 3) Whenever a PDP context is destroyed, this deletes the tunnel via genetlink GTP interface. 4) Destroy the gtp0 device if ggsn is stopped, including all of the existing tunnels.
You require the osmo-ggsn.git tree, which contains the kernel module gtp.ko and the libgtpnl library that you have to compile and install. Make sure you have loaded the gtp.ko kernel module before launching the ggsn daemon using the kernel driver mode, otherwise you will get a nice "operation not supported" error message ;-).
This patch also adds supports for "ipup" configuration option to invoke an external script after the gtp0 device has been brought up. Typical command to add the route to reach the MS behind the GGSN is required, eg. ip route add 10.0.0.0/8 dev gtp0.
The (horrible) ggsn parser has been manually extended to support the new configuration option. That code doesn't look nice, but it just mimics what we already have there for consistency, please don't blame me for that.
If you want to run in debugging mode, I suggest you to use:
sudo ggsn -c ggsn.conf -f -d
Note that you do have to run openggsn as root to bring up the gtp0 device. You have to see this message that announce that the GTP kernel mode is enabled.
openggsn[1106]: ggsn.c: 656: Using the GTP kernel mode (genl ID is 25)
This patch also automagically sets up route to reach MS from Internet just like tun mode does. This is fundamental to get this working, better don't leave to the admin, he may forget to add this route.
In this patch, I tried to encapsulate this new feature as much as possible as Harald initially suggested.
To compile this feature, you have to pass --enable-gtp-kernel, ie.
./configire --enable-gtp-kernel
Otherwise, the code to interact with the gtp kernel part is not compiled.
Signed-off-by: Andreas Schultz aschultz@tpip.net --- configure.ac | 15 ++++ ggsn/Makefile.am | 11 ++- ggsn/cmdline.c | 39 ++++++--- ggsn/cmdline.ggo | 3 + ggsn/cmdline.h | 8 +- ggsn/ggsn.c | 35 +++++++- ggsn/gtp-kernel.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ggsn/gtp-kernel.h | 51 ++++++++++++ 8 files changed, 384 insertions(+), 18 deletions(-) create mode 100644 ggsn/gtp-kernel.c create mode 100644 ggsn/gtp-kernel.h
diff --git a/configure.ac b/configure.ac index e31a79e..1fad6b1 100644 --- a/configure.ac +++ b/configure.ac @@ -45,6 +45,17 @@ AC_SUBST(EXEC_LDADD) # FIXME: Replace `main' with a function in `-links': #AC_CHECK_LIB([inks], [main])
+dnl GTP kernel dependencies +AC_ARG_ENABLE([gtp-kernel], + AS_HELP_STRING([--enable-gtp-kernel], [Build GTP tunneling kernel]), + [enable_gtp_kernel="$enableval"], [enable_gtp_kernel="no"]) + +if test "x$enable_gtp_kernel" = "xyes"; then + PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.0.0]) +fi + +AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_kernel" = "yes"]) + # Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT @@ -119,3 +130,7 @@ AC_CONFIG_FILES([Makefile libgtp.pc openggsn.spec]) AC_OUTPUT + +echo " +openggsn Configuration: + GTP kernel support: ${enable_gtp_kernel}" diff --git a/ggsn/Makefile.am b/ggsn/Makefile.am index 91df8f0..c8868c1 100644 --- a/ggsn/Makefile.am +++ b/ggsn/Makefile.am @@ -4,7 +4,16 @@ AM_LDFLAGS = @EXEC_LDFLAGS@
AM_CFLAGS = -O2 -D_GNU_SOURCE -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
+if ENABLE_GTP_KERNEL +AM_CFLAGS += -DGTP_KERNEL +ggsn_LDADD = @EXEC_LDADD@ -lgtp -lgtpnl -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) +else ggsn_LDADD = @EXEC_LDADD@ -lgtp -L../gtp ../lib/libmisc.a $(LIBOSMOCORE_LIBS) +endif + ggsn_DEPENDENCIES = ../gtp/libgtp.la ../lib/libmisc.a -ggsn_SOURCES = ggsn.c cmdline.c cmdline.h +ggsn_SOURCES = ggsn.c cmdline.c cmdline.h gtp-kernel.h
+if ENABLE_GTP_KERNEL +ggsn_SOURCES += gtp-kernel.c +endif diff --git a/ggsn/cmdline.c b/ggsn/cmdline.c index 37ed992..a4c25d8 100644 --- a/ggsn/cmdline.c +++ b/ggsn/cmdline.c @@ -1,5 +1,5 @@ /* - File autogenerated by gengetopt version 2.22.6 + File autogenerated by gengetopt version 2.22.5 generated with the following command: gengetopt --conf-parser
@@ -29,8 +29,6 @@ const char *gengetopt_args_info_purpose = "";
const char *gengetopt_args_info_usage = "Usage: " CMDLINE_PARSER_PACKAGE " [OPTIONS]...";
-const char *gengetopt_args_info_versiontext = ""; - const char *gengetopt_args_info_description = "";
const char *gengetopt_args_info_help[] = { @@ -39,8 +37,8 @@ const char *gengetopt_args_info_help[] = { " -f, --fg Run in foreground (default=off)", " -d, --debug Run in debug mode (default=off)", " -c, --conf=STRING Read configuration file (default=`/etc/ggsn.conf')", - " --pidfile=STRING Filename of process id file\n (default=`/var/run/ggsn.pid')", - " --statedir=STRING Directory of nonvolatile data\n (default=`/var/lib/ggsn/')", + " --pidfile=STRING Filename of process id file \n (default=`/var/run/ggsn.pid')", + " --statedir=STRING Directory of nonvolatile data \n (default=`/var/lib/ggsn/')", " -l, --listen=STRING Local interface", " -n, --net=STRING Network (default=`192.168.0.0/24')", " --ipup=STRING Script to run after link-up", @@ -54,6 +52,7 @@ const char *gengetopt_args_info_help[] = { " -q, --qos=INT Requested quality of service (default=`0x0b921f')", " --logfile=STRING Logfile for errors", " --loglevel=STRING Global log ldevel (default=`error')", + " -g, --gtpnl=STRING GTP kernel support (default=`eth0')", 0 };
@@ -123,6 +122,7 @@ void clear_given (struct gengetopt_args_info *args_info) args_info->qos_given = 0 ; args_info->logfile_given = 0 ; args_info->loglevel_given = 0 ; + args_info->gtpnl_given = 0 ; }
static @@ -163,6 +163,8 @@ void clear_args (struct gengetopt_args_info *args_info) args_info->logfile_orig = NULL; args_info->loglevel_arg = gengetopt_strdup ("error"); args_info->loglevel_orig = NULL; + args_info->gtpnl_arg = gengetopt_strdup ("eth0"); + args_info->gtpnl_orig = NULL;
}
@@ -191,6 +193,7 @@ void init_args_info(struct gengetopt_args_info *args_info) args_info->qos_help = gengetopt_args_info_help[17] ; args_info->logfile_help = gengetopt_args_info_help[18] ; args_info->loglevel_help = gengetopt_args_info_help[19] ; + args_info->gtpnl_help = gengetopt_args_info_help[19] ;
}
@@ -200,9 +203,6 @@ cmdline_parser_print_version (void) printf ("%s %s\n", (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), CMDLINE_PARSER_VERSION); - - if (strlen(gengetopt_args_info_versiontext) > 0) - printf("\n%s\n", gengetopt_args_info_versiontext); }
static void print_help_common(void) { @@ -304,6 +304,8 @@ cmdline_parser_release (struct gengetopt_args_info *args_info) free_string_field (&(args_info->logfile_orig)); free_string_field (&(args_info->loglevel_arg)); free_string_field (&(args_info->loglevel_orig)); + free_string_field (&(args_info->gtpnl_arg)); + free_string_field (&(args_info->gtpnl_orig));
@@ -374,6 +376,8 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) write_into_file(outfile, "logfile", args_info->logfile_orig, 0); if (args_info->loglevel_given) write_into_file(outfile, "loglevel", args_info->loglevel_orig, 0); + if (args_info->gtpnl_given) + write_into_file(outfile, "gtpnl", args_info->gtpnl_orig, 0);
i = EXIT_SUCCESS; @@ -598,7 +602,7 @@ cmdline_parser_internal ( { int c; /* Character of the parsed option. */
- int error_occurred = 0; + int error = 0; struct gengetopt_args_info local_args_info;
int override; @@ -648,10 +652,11 @@ cmdline_parser_internal ( { "qos", 1, NULL, 'q' }, { "logfile", 1, NULL, 0 }, { "loglevel", 1, NULL, 0 }, + { "gtpnl", 1, NULL, 'g' }, { 0, 0, 0, 0 } };
- c = getopt_long (argc, argv, "hVfdc:l:n:a:q:", long_options, &option_index); + c = getopt_long (argc, argv, "hVfdc:l:n:a:q:g:", long_options, &option_index);
if (c == -1) break; /* Exit from `while (1)' loop. */
@@ -747,6 +752,18 @@ cmdline_parser_internal ( goto failure;
break; + case 'g': /* GTP kernel support. */ + + + if (update_arg( (void *)&(args_info->gtpnl_arg), + &(args_info->gtpnl_orig), &(args_info->gtpnl_given), + &(local_args_info.gtpnl_given), optarg, 0, "eth0", ARG_STRING, + check_ambiguity, override, 0, 0, + "gtpnl", 'g', + additional_error)) + goto failure; + + break;
case 0: /* Long option with no short option */ /* Filename of process id file. */ @@ -920,7 +937,7 @@ cmdline_parser_internal (
cmdline_parser_release (&local_args_info);
- if ( error_occurred ) + if ( error ) return (EXIT_FAILURE);
return 0; diff --git a/ggsn/cmdline.ggo b/ggsn/cmdline.ggo index 9c4c976..47ff102 100644 --- a/ggsn/cmdline.ggo +++ b/ggsn/cmdline.ggo @@ -33,3 +33,6 @@ option "apn" a "Access point name" string default="internet option "qos" q "Requested quality of service" int default="0x0b921f" no option "logfile" - "Logfile for errors" string no option "loglevel" - "Global log ldevel" string default="error" no + +option "gtpnl" g "GTP kernel support" string default="eth0" no + diff --git a/ggsn/cmdline.h b/ggsn/cmdline.h index a87fa4a..150fb4d 100644 --- a/ggsn/cmdline.h +++ b/ggsn/cmdline.h @@ -1,6 +1,6 @@ /** @file cmdline.h * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.6 + * generated by GNU Gengetopt version 2.22.5 * http://www.gnu.org/software/gengetopt. * DO NOT modify this file, since it can be overwritten * @author GNU Gengetopt by Lorenzo Bettini */ @@ -95,6 +95,9 @@ struct gengetopt_args_info char * loglevel_arg; /**< @brief Global log ldevel (default='error'). */ char * loglevel_orig; /**< @brief Global log ldevel original value given at command line. */ const char *loglevel_help; /**< @brief Global log ldevel help description. */ + char * gtpnl_arg; /**< @brief GTP kernel support (default='eth0'). */ + char * gtpnl_orig; /**< @brief GTP kernel support original value given at command line. */ + const char *gtpnl_help; /**< @brief GTP kernel support help description. */
unsigned int help_given ; /**< @brief Whether help was given. */ unsigned int version_given ; /**< @brief Whether version was given. */ @@ -116,6 +119,7 @@ struct gengetopt_args_info unsigned int qos_given ; /**< @brief Whether qos was given. */ unsigned int logfile_given ; /**< @brief Whether logfile was given. */ unsigned int loglevel_given ; /**< @brief Whether loglevel was given. */ + unsigned int gtpnl_given ; /**< @brief Whether gtpnl was given. */
} ;
@@ -133,8 +137,6 @@ struct cmdline_parser_params extern const char *gengetopt_args_info_purpose; /** @brief the usage string of the program */ extern const char *gengetopt_args_info_usage; -/** @brief the description string of the program */ -extern const char *gengetopt_args_info_description; /** @brief all the lines making the help output */ extern const char *gengetopt_args_info_help[];
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 53c8c01..821c942 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -54,6 +54,7 @@ #include "../gtp/pdp.h" #include "../gtp/gtp.h" #include "cmdline.h" +#include "gtp-kernel.h"
int end = 0; int maxfd = 0; /* For select() */ @@ -134,6 +135,13 @@ int delete_context(struct pdp_t *pdp) ippool_freeip(ippool, (struct ippoolm_t *)pdp->peer); else SYS_ERR(DGGSN, LOGL_ERROR, 0, "Peer not defined!"); + + if (gtp_kernel_tunnel_del(pdp)) { + SYS_ERR(DGGSN, LOGL_ERROR, 0, + "Cannot delete tunnel from kernel: %s\n", + strerror(errno)); + } + return 0; }
@@ -167,6 +175,11 @@ int create_context_ind(struct pdp_t *pdp) pdp->ipif = tun; /* TODO */ member->peer = pdp;
+ if (gtp_kernel_tunnel_add(pdp) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "Cannot add tunnel to kernel: %s\n", strerror(errno)); + } + gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ); return 0; /* Success */ } @@ -247,6 +260,8 @@ int main(int argc, char **argv) printf("pidfile: %s\n", args_info.pidfile_arg); if (args_info.statedir_arg) printf("statedir: %s\n", args_info.statedir_arg); + if (args_info.gtpnl_arg) + printf("gtpnl: %s\n", args_info.gtpnl_arg); printf("timelimit: %d\n", args_info.timelimit_arg); }
@@ -307,6 +322,8 @@ int main(int argc, char **argv) printf("pidfile: %s\n", args_info.pidfile_arg); if (args_info.statedir_arg) printf("statedir: %s\n", args_info.statedir_arg); + if (args_info.gtpnl_arg) + printf("gtpnl: %s\n", args_info.gtpnl_arg); printf("timelimit: %d\n", args_info.timelimit_arg); }
@@ -502,10 +519,18 @@ int main(int argc, char **argv) if (gsn->fd1u > maxfd) maxfd = gsn->fd1u;
+ /* use GTP kernel module for data packet encapsulation */ + if (gtp_kernel_init(gsn, &net, &mask, &args_info) < 0) + goto err; + gtp_set_cb_data_ind(gsn, encaps_tun); gtp_set_cb_delete_context(gsn, delete_context); gtp_set_cb_create_context_ind(gsn, create_context_ind);
+ /* skip the configuration of the tun0 if we're using the gtp0 device */ + if (gtp_kernel_enabled()) + goto skip_tun; + /* Create a tunnel interface */ DEBUGP(DGGSN, "Creating tun interface\n"); if (tun_new((struct tun_t **)&tun)) { @@ -526,6 +551,8 @@ int main(int argc, char **argv) if (ipup) tun_runscript(tun, ipup);
+skip_tun: + /******************************************************************/ /* Main select loop */ /******************************************************************/ @@ -556,7 +583,7 @@ int main(int argc, char **argv) break; }
- if (tun->fd != -1 && FD_ISSET(tun->fd, &fds) && + if (tun && tun->fd != -1 && FD_ISSET(tun->fd, &fds) && tun_decaps(tun) < 0) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "TUN read failed (fd)=(%d)", tun->fd); @@ -572,11 +599,13 @@ int main(int argc, char **argv) gtp_decaps1u(gsn);
} - +err: + gtp_kernel_stop(); cmdline_parser_free(&args_info); ippool_free(ippool); gtp_free(gsn); - tun_free(tun); + if (tun) + tun_free(tun);
return 1;
diff --git a/ggsn/gtp-kernel.c b/ggsn/gtp-kernel.c new file mode 100644 index 0000000..16e51ac --- /dev/null +++ b/ggsn/gtp-kernel.c @@ -0,0 +1,240 @@ +#ifdef __linux__ +#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */ +#endif + +#include "../config.h" + +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif + +#include <syslog.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <libgtpnl/gtp.h> +#include <libgtpnl/gtpnl.h> +#include <libmnl/libmnl.h> + +#include <errno.h> + +#include <time.h> + +#include "../lib/tun.h" +#include "../lib/syserr.h" +#include "../gtp/pdp.h" +#include "../gtp/gtp.h" +#include "cmdline.h" + +#include <libgtpnl/gtp.h> +#include <libgtpnl/gtpnl.h> +#include <libmnl/libmnl.h> + +#include "gtp-kernel.h" + +static void pdp_debug(struct pdp_t *pdp) +{ + int i; + uint64_t teid; + + if (!debug) + return; + + printf("version %u\n", pdp->version); + if (pdp->version == 0) { + teid = pdp_gettid(pdp->imsi, pdp->nsapi); + printf("flowid %u\n", pdp->flru); + } else { + teid = pdp->teid_gn; /* GTPIE_TEI_DI */ + } + + printf("teid %llx\n", teid); + printf("address (%u)\n", pdp->eua.l); + + /* Byte 0: 0xf1 == IETF */ + /* Byte 1: 0x21 == IPv4 */ + /* Byte 2-6: IPv4 address */ + + for (i = 0; i < 6; i++) + printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */ + + printf("\n"); + printf("sgsn-addr (%u)\n", pdp->gsnrc.l); + + for (i = 0; i < 4; i++) + printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */ + + printf("\n"); +} + +static int mask2prefix(struct in_addr *mask) +{ + uint32_t tmp = ntohl(mask->s_addr); + int k; + + for (k=0; tmp > 0; k++) + tmp = (tmp << 1); + + return k; +} + +static struct { + int genl_id; + struct mnl_socket *nl; + bool enabled; +} gtp_nl; + +/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */ +#define GTP_DEVNAME "gtp0" + +int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, + struct in_addr *mask, + struct gengetopt_args_info *args_info) +{ + if (!args_info->gtpnl_given) + return 0; + + if (gtp_dev_create(GTP_DEVNAME, args_info->gtpnl_orig, + gsn->fd0, gsn->fd1u) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "cannot create GTP tunnel device: %s\n", + strerror(errno)); + return -1; + } + gtp_nl.enabled = true; + + gtp_nl.nl = genl_socket_open(); + if (gtp_nl.nl == NULL) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "cannot create genetlink socket\n"); + return -1; + } + gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp"); + if (gtp_nl.genl_id < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "cannot lookup GTP genetlink ID\n"); + return -1; + } + if (debug) { + sys_err(LOG_NOTICE, __FILE__, __LINE__, 0, + "Using the GTP kernel mode (genl ID is %d)\n", + gtp_nl.genl_id); + } + + if (debug) { + printf("Setting route to reach %s via %s\n", + args_info->net_arg, GTP_DEVNAME); + } + + if (gtp_dev_config(GTP_DEVNAME, net, mask2prefix(mask)) < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "Cannot add route to reach network %s\n", + args_info->net_arg); + } + + /* launch script if it is set to bring up the route to reach + * the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this + * using native rtnetlink interface given that we know the + * MS network mask, later. + */ + if (ipup) { + char cmd[1024]; + int err; + + /* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */ + snprintf(cmd, sizeof(cmd), "%s %s %s", + ipup, GTP_DEVNAME, args_info->net_arg); + cmd[sizeof(cmd)-1] = '\0'; + + err = system(cmd); + if (err < 0) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "Failed to launch script `%s'", ipup); + return -1; + } + } + sys_err(LOG_NOTICE, __FILE__, __LINE__, 0, "GTP kernel configured\n"); + + return 0; +} + +void gtp_kernel_stop(void) +{ + if (!gtp_nl.enabled) + return; + + gtp_dev_destroy(GTP_DEVNAME); +} + +int gtp_kernel_tunnel_add(struct pdp_t *pdp) +{ + struct in_addr ms, sgsn; + struct gtp_tunnel *t; + int ret; + + if (!gtp_nl.enabled) + return 0; + + pdp_debug(pdp); + + t = gtp_tunnel_alloc(); + if (t == NULL) + return -1; + + memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr)); + memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr)); + + gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME)); + gtp_tunnel_set_version(t, pdp->version); + gtp_tunnel_set_ms_ip4(t, &ms); + gtp_tunnel_set_sgsn_ip4(t, &sgsn); + if (pdp->version == 0) { + gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); + gtp_tunnel_set_flowid(t, pdp->flru); + } else { + gtp_tunnel_set_tid(t, pdp->teid_gn); /* GTPIE_TEI_DI */ + } + + ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); + gtp_tunnel_free(t); + + return ret; +} + +int gtp_kernel_tunnel_del(struct pdp_t *pdp) +{ + struct gtp_tunnel *t; + int ret; + + if (!gtp_nl.enabled) + return 0; + + pdp_debug(pdp); + + t = gtp_tunnel_alloc(); + if (t == NULL) + return -1; + + gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME)); + gtp_tunnel_set_version(t, pdp->version); + if (pdp->version == 0) { + gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); + gtp_tunnel_set_flowid(t, pdp->flru); + } else { + gtp_tunnel_set_tid(t, pdp->teid_gn); /* GTPIE_TEI_DI */ + } + + ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t); + gtp_tunnel_free(t); + + return ret; +} + +int gtp_kernel_enabled(void) +{ + return gtp_nl.enabled; +} diff --git a/ggsn/gtp-kernel.h b/ggsn/gtp-kernel.h new file mode 100644 index 0000000..7bf533d --- /dev/null +++ b/ggsn/gtp-kernel.h @@ -0,0 +1,51 @@ +#ifndef _GTP_KERNEL_H_ +#define _GTP_KERNEL_H_ + +struct gengetopt_args_info; + +extern int debug; +extern char *ipup; + +#ifdef GTP_KERNEL +int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, + struct in_addr *mask, + struct gengetopt_args_info *args_info); +void gtp_kernel_stop(void); + +int gtp_kernel_tunnel_add(struct pdp_t *pdp); +int gtp_kernel_tunnel_del(struct pdp_t *pdp); + +int gtp_kernel_enabled(void); + +#else +static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, + struct in_addr *mask, + struct gengetopt_args_info *args_info) +{ + if (args_info->gtpnl_given) { + sys_err(LOG_ERR, __FILE__, __LINE__, 0, + "ggsn compiled without GTP kernel support!\n"); + return -1; + } + return 0; +} + +static inline void gtp_kernel_stop(void) {} + +static inline int gtp_kernel_tunnel_add(struct pdp_t *pdp) +{ + return 0; +} + +static inline int gtp_kernel_tunnel_del(struct pdp_t *pdp) +{ + return 0; +} + +static inline int gtp_kernel_enabled(void) +{ + return 0; +} + +#endif +#endif /* _GTP_KERNEL_H_ */
On Tue, Nov 17, 2015 at 12:22:42PM +0100, Andreas Schultz wrote:
From: Pablo Neira Ayuso pablo@gnumonks.org
This patch adds the -g, --gtpnl=device option that allows you to enable the GTP kernel tunneling mode in openggsn. You have to specify the real downlink device that will be used to tunnel traffic, eg.
-g=eth0
This is basically my original patch rebased upon master or are there any changes on it?
----- Original Message -----
From: "Pablo Neira Ayuso" pablo@soleta.eu To: "Andreas Schultz" aschultz@tpip.net Cc: openbsc@lists.osmocom.org, "Harald Welte" laforge@gnumonks.org, "Pablo Neira Ayuso" pablo@gnumonks.org Sent: Tuesday, November 17, 2015 1:37:51 PM Subject: Re: [PATCH openggsn 1/4] ggsn: add support for GTP kernel data encapsulation
On Tue, Nov 17, 2015 at 12:22:42PM +0100, Andreas Schultz wrote:
From: Pablo Neira Ayuso pablo@gnumonks.org
This patch adds the -g, --gtpnl=device option that allows you to enable the GTP kernel tunneling mode in openggsn. You have to specify the real downlink device that will be used to tunnel traffic, eg.
-g=eth0
This is basically my original patch rebased upon master or are there any changes on it?
No changes, I wanted to keep you original for reference, then apply the two fixes on top of that and add the netns stuff after that.
You patch doesn't build anymore, due to the logging changes. If you want every changeset to build-able, you need to merge it with the log and automake patches.
Andreas
On Tue, Nov 17, 2015 at 01:44:46PM +0100, Andreas Schultz wrote:
----- Original Message -----
From: "Pablo Neira Ayuso" pablo@soleta.eu To: "Andreas Schultz" aschultz@tpip.net Cc: openbsc@lists.osmocom.org, "Harald Welte" laforge@gnumonks.org, "Pablo Neira Ayuso" pablo@gnumonks.org Sent: Tuesday, November 17, 2015 1:37:51 PM Subject: Re: [PATCH openggsn 1/4] ggsn: add support for GTP kernel data encapsulation
On Tue, Nov 17, 2015 at 12:22:42PM +0100, Andreas Schultz wrote:
From: Pablo Neira Ayuso pablo@gnumonks.org
This patch adds the -g, --gtpnl=device option that allows you to enable the GTP kernel tunneling mode in openggsn. You have to specify the real downlink device that will be used to tunnel traffic, eg.
-g=eth0
This is basically my original patch rebased upon master or are there any changes on it?
No changes, I wanted to keep you original for reference, then apply the two fixes on top of that and add the netns stuff after that.
You patch doesn't build anymore, due to the logging changes. If you want every changeset to build-able, you need to merge it with the log and automake patches.
Makes sense, thanks.
@Harald, any objection if I push this patchset?
Thanks.
Hi Pablo and Andreas,
On Tue, Nov 17, 2015 at 02:12:37PM +0100, Pablo Neira Ayuso wrote:
@Harald, any objection if I push this patchset?
not at all!
Signed-off-by: Andreas Schultz aschultz@tpip.net --- ggsn/ggsn.c | 2 +- ggsn/gtp-kernel.c | 21 +++++++++------------ ggsn/gtp-kernel.h | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-)
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 821c942..9e8e213 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -176,7 +176,7 @@ int create_context_ind(struct pdp_t *pdp) member->peer = pdp;
if (gtp_kernel_tunnel_add(pdp) < 0) { - sys_err(LOG_ERR, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_ERROR, 0, "Cannot add tunnel to kernel: %s\n", strerror(errno)); }
diff --git a/ggsn/gtp-kernel.c b/ggsn/gtp-kernel.c index 16e51ac..487ae35 100644 --- a/ggsn/gtp-kernel.c +++ b/ggsn/gtp-kernel.c @@ -8,7 +8,6 @@ #include <stdint.h> #endif
-#include <syslog.h> #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -100,7 +99,7 @@ int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
if (gtp_dev_create(GTP_DEVNAME, args_info->gtpnl_orig, gsn->fd0, gsn->fd1u) < 0) { - sys_err(LOG_ERR, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_ERROR, 0, "cannot create GTP tunnel device: %s\n", strerror(errno)); return -1; @@ -109,29 +108,27 @@ int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
gtp_nl.nl = genl_socket_open(); if (gtp_nl.nl == NULL) { - sys_err(LOG_ERR, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_ERROR, 0, "cannot create genetlink socket\n"); return -1; } gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp"); if (gtp_nl.genl_id < 0) { - sys_err(LOG_ERR, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_ERROR, 0, "cannot lookup GTP genetlink ID\n"); return -1; } if (debug) { - sys_err(LOG_NOTICE, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_NOTICE, 0, "Using the GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id); }
- if (debug) { - printf("Setting route to reach %s via %s\n", - args_info->net_arg, GTP_DEVNAME); - } + DEBUGP(DGGSN, "Setting route to reach %s via %s\n", + args_info->net_arg, GTP_DEVNAME);
if (gtp_dev_config(GTP_DEVNAME, net, mask2prefix(mask)) < 0) { - sys_err(LOG_ERR, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_ERROR, 0, "Cannot add route to reach network %s\n", args_info->net_arg); } @@ -152,12 +149,12 @@ int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
err = system(cmd); if (err < 0) { - sys_err(LOG_ERR, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to launch script `%s'", ipup); return -1; } } - sys_err(LOG_NOTICE, __FILE__, __LINE__, 0, "GTP kernel configured\n"); + SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
return 0; } diff --git a/ggsn/gtp-kernel.h b/ggsn/gtp-kernel.h index 7bf533d..628002f 100644 --- a/ggsn/gtp-kernel.h +++ b/ggsn/gtp-kernel.h @@ -23,7 +23,7 @@ static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, struct gengetopt_args_info *args_info) { if (args_info->gtpnl_given) { - sys_err(LOG_ERR, __FILE__, __LINE__, 0, + SYS_ERR(DGGSN, LOGL_ERROR, 0, "ggsn compiled without GTP kernel support!\n"); return -1; }
see https://autotools.io/pkgconfig/pkg_check_modules.html, Optional Modules for explanation
Signed-off-by: Andreas Schultz aschultz@tpip.net --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac index 1fad6b1..3f5148a 100644 --- a/configure.ac +++ b/configure.ac @@ -50,9 +50,9 @@ AC_ARG_ENABLE([gtp-kernel], AS_HELP_STRING([--enable-gtp-kernel], [Build GTP tunneling kernel]), [enable_gtp_kernel="$enableval"], [enable_gtp_kernel="no"])
-if test "x$enable_gtp_kernel" = "xyes"; then +AS_IF([test "x$enable_gtp_kernel" = "xyes"], [ PKG_CHECK_MODULES([LIBGTPNL], [libgtpnl >= 1.0.0]) -fi +])
AM_CONDITIONAL([ENABLE_GTP_KERNEL], [test "$enable_gtp_kernel" = "yes"])
The kernel gtp can now be create in an existing sub namespace.
Signed-off-by: Andreas Schultz aschultz@tpip.net --- ggsn/cmdline.c | 41 ++++++++++++++--- ggsn/cmdline.ggo | 1 + ggsn/cmdline.h | 8 +++- ggsn/ggsn.c | 19 +++++++- ggsn/gtp-kernel.c | 33 ++++++++++--- ggsn/gtp-kernel.h | 6 ++- lib/Makefile.am | 4 +- lib/netns.c | 135 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/netns.h | 26 +++++++++++ 9 files changed, 255 insertions(+), 18 deletions(-) create mode 100644 lib/netns.c create mode 100644 lib/netns.h
diff --git a/ggsn/cmdline.c b/ggsn/cmdline.c index a4c25d8..a7572af 100644 --- a/ggsn/cmdline.c +++ b/ggsn/cmdline.c @@ -1,5 +1,5 @@ /* - File autogenerated by gengetopt version 2.22.5 + File autogenerated by gengetopt version 2.22.6 generated with the following command: gengetopt --conf-parser
@@ -29,6 +29,8 @@ 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[] = { @@ -37,8 +39,8 @@ const char *gengetopt_args_info_help[] = { " -f, --fg Run in foreground (default=off)", " -d, --debug Run in debug mode (default=off)", " -c, --conf=STRING Read configuration file (default=`/etc/ggsn.conf')", - " --pidfile=STRING Filename of process id file \n (default=`/var/run/ggsn.pid')", - " --statedir=STRING Directory of nonvolatile data \n (default=`/var/lib/ggsn/')", + " --pidfile=STRING Filename of process id file\n (default=`/var/run/ggsn.pid')", + " --statedir=STRING Directory of nonvolatile data\n (default=`/var/lib/ggsn/')", " -l, --listen=STRING Local interface", " -n, --net=STRING Network (default=`192.168.0.0/24')", " --ipup=STRING Script to run after link-up", @@ -53,6 +55,7 @@ const char *gengetopt_args_info_help[] = { " --logfile=STRING Logfile for errors", " --loglevel=STRING Global log ldevel (default=`error')", " -g, --gtpnl=STRING GTP kernel support (default=`eth0')", + " --gtpns=STRING Namespace for GTP interface", 0 };
@@ -123,6 +126,7 @@ void clear_given (struct gengetopt_args_info *args_info) args_info->logfile_given = 0 ; args_info->loglevel_given = 0 ; args_info->gtpnl_given = 0 ; + args_info->gtpns_given = 0 ; }
static @@ -165,6 +169,8 @@ void clear_args (struct gengetopt_args_info *args_info) args_info->loglevel_orig = NULL; args_info->gtpnl_arg = gengetopt_strdup ("eth0"); args_info->gtpnl_orig = NULL; + args_info->gtpns_arg = NULL; + args_info->gtpns_orig = NULL;
}
@@ -193,7 +199,8 @@ void init_args_info(struct gengetopt_args_info *args_info) args_info->qos_help = gengetopt_args_info_help[17] ; args_info->logfile_help = gengetopt_args_info_help[18] ; args_info->loglevel_help = gengetopt_args_info_help[19] ; - args_info->gtpnl_help = gengetopt_args_info_help[19] ; + args_info->gtpnl_help = gengetopt_args_info_help[20] ; + args_info->gtpns_help = gengetopt_args_info_help[21] ;
}
@@ -203,6 +210,9 @@ 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) { @@ -306,6 +316,8 @@ cmdline_parser_release (struct gengetopt_args_info *args_info) free_string_field (&(args_info->loglevel_orig)); free_string_field (&(args_info->gtpnl_arg)); free_string_field (&(args_info->gtpnl_orig)); + free_string_field (&(args_info->gtpns_arg)); + free_string_field (&(args_info->gtpns_orig));
@@ -378,6 +390,8 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) write_into_file(outfile, "loglevel", args_info->loglevel_orig, 0); if (args_info->gtpnl_given) write_into_file(outfile, "gtpnl", args_info->gtpnl_orig, 0); + if (args_info->gtpns_given) + write_into_file(outfile, "gtpns", args_info->gtpns_orig, 0);
i = EXIT_SUCCESS; @@ -602,7 +616,7 @@ cmdline_parser_internal ( { int c; /* Character of the parsed option. */
- int error = 0; + int error_occurred = 0; struct gengetopt_args_info local_args_info;
int override; @@ -653,6 +667,7 @@ cmdline_parser_internal ( { "logfile", 1, NULL, 0 }, { "loglevel", 1, NULL, 0 }, { "gtpnl", 1, NULL, 'g' }, + { "gtpns", 1, NULL, 0 }, { 0, 0, 0, 0 } };
@@ -920,6 +935,20 @@ cmdline_parser_internal ( goto failure;
} + /* Namespace for GTP interface. */ + else if (strcmp (long_options[option_index].name, "gtpns") == 0) + { + + + if (update_arg( (void *)&(args_info->gtpns_arg), + &(args_info->gtpns_orig), &(args_info->gtpns_given), + &(local_args_info.gtpns_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "gtpns", '-', + additional_error)) + goto failure; + + }
break; case '?': /* Invalid option. */ @@ -937,7 +966,7 @@ cmdline_parser_internal (
cmdline_parser_release (&local_args_info);
- if ( error ) + if ( error_occurred ) return (EXIT_FAILURE);
return 0; diff --git a/ggsn/cmdline.ggo b/ggsn/cmdline.ggo index 47ff102..a7d876c 100644 --- a/ggsn/cmdline.ggo +++ b/ggsn/cmdline.ggo @@ -35,4 +35,5 @@ option "logfile" - "Logfile for errors" string no option "loglevel" - "Global log ldevel" string default="error" no
option "gtpnl" g "GTP kernel support" string default="eth0" no +option "gtpns" - "Namespace for GTP interface" string no
diff --git a/ggsn/cmdline.h b/ggsn/cmdline.h index 150fb4d..77d60c2 100644 --- a/ggsn/cmdline.h +++ b/ggsn/cmdline.h @@ -1,6 +1,6 @@ /** @file cmdline.h * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.5 + * 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 */ @@ -98,6 +98,9 @@ struct gengetopt_args_info char * gtpnl_arg; /**< @brief GTP kernel support (default='eth0'). */ char * gtpnl_orig; /**< @brief GTP kernel support original value given at command line. */ const char *gtpnl_help; /**< @brief GTP kernel support help description. */ + char * gtpns_arg; /**< @brief Namespace for GTP interface. */ + char * gtpns_orig; /**< @brief Namespace for GTP interface original value given at command line. */ + const char *gtpns_help; /**< @brief Namespace for GTP interface help description. */
unsigned int help_given ; /**< @brief Whether help was given. */ unsigned int version_given ; /**< @brief Whether version was given. */ @@ -120,6 +123,7 @@ struct gengetopt_args_info unsigned int logfile_given ; /**< @brief Whether logfile was given. */ unsigned int loglevel_given ; /**< @brief Whether loglevel was given. */ unsigned int gtpnl_given ; /**< @brief Whether gtpnl was given. */ + unsigned int gtpns_given ; /**< @brief Whether gtpns was given. */
} ;
@@ -137,6 +141,8 @@ struct cmdline_parser_params extern const char *gengetopt_args_info_purpose; /** @brief the usage string of the program */ extern const char *gengetopt_args_info_usage; +/** @brief the description string of the program */ +extern const char *gengetopt_args_info_description; /** @brief all the lines making the help output */ extern const char *gengetopt_args_info_help[];
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c index 9e8e213..f823552 100644 --- a/ggsn/ggsn.c +++ b/ggsn/ggsn.c @@ -48,6 +48,7 @@
#include <time.h>
+#include "../lib/netns.h" #include "../lib/tun.h" #include "../lib/ippool.h" #include "../lib/syserr.h" @@ -59,6 +60,8 @@ int end = 0; int maxfd = 0; /* For select() */
+int gtp_ns = 0; + struct in_addr listen_; struct in_addr netaddr, destaddr, net, mask; /* Network interface */ struct in_addr dns1, dns2; /* PCO DNS address */ @@ -262,6 +265,8 @@ int main(int argc, char **argv) printf("statedir: %s\n", args_info.statedir_arg); if (args_info.gtpnl_arg) printf("gtpnl: %s\n", args_info.gtpnl_arg); + if (args_info.gtpns_arg) + printf("gtpns: %s\n", args_info.gtpns_arg); printf("timelimit: %d\n", args_info.timelimit_arg); }
@@ -324,6 +329,8 @@ int main(int argc, char **argv) printf("statedir: %s\n", args_info.statedir_arg); if (args_info.gtpnl_arg) printf("gtpnl: %s\n", args_info.gtpnl_arg); + if (args_info.gtpns_arg) + printf("gtpns: %s\n", args_info.gtpns_arg); printf("timelimit: %d\n", args_info.timelimit_arg); }
@@ -506,6 +513,16 @@ int main(int argc, char **argv) log_pid(args_info.pidfile_arg); }
+ init_netns(); + if (args_info.gtpns_arg) { + DEBUGP(DGGSN, "gtpclient: Initialising Network Namespace\n"); + + if ((gtp_ns = get_nsfd(args_info.gtpns_arg)) <= 0) { + SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to initialize network namespace"); + exit(1); + } + } + DEBUGP(DGGSN, "gtpclient: Initialising GTP tunnel\n");
if (gtp_new(&gsn, args_info.statedir_arg, &listen_, GTP_MODE_GGSN)) { @@ -520,7 +537,7 @@ int main(int argc, char **argv) maxfd = gsn->fd1u;
/* use GTP kernel module for data packet encapsulation */ - if (gtp_kernel_init(gsn, &net, &mask, &args_info) < 0) + if (gtp_kernel_init(gtp_ns, gsn, &net, &mask, &args_info) < 0) goto err;
gtp_set_cb_data_ind(gsn, encaps_tun); diff --git a/ggsn/gtp-kernel.c b/ggsn/gtp-kernel.c index 487ae35..3dabdb4 100644 --- a/ggsn/gtp-kernel.c +++ b/ggsn/gtp-kernel.c @@ -23,6 +23,7 @@
#include <time.h>
+#include "../lib/netns.h" #include "../lib/tun.h" #include "../lib/syserr.h" #include "../gtp/pdp.h" @@ -82,6 +83,8 @@ static int mask2prefix(struct in_addr *mask) }
static struct { + int ns; + int ifidx; int genl_id; struct mnl_socket *nl; bool enabled; @@ -90,21 +93,27 @@ static struct { /* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */ #define GTP_DEVNAME "gtp0"
-int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, +int gtp_kernel_init(int ns, + struct gsn_t *gsn, struct in_addr *net, struct in_addr *mask, struct gengetopt_args_info *args_info) { + unsigned int ret = 0; + sigset_t oldmask; + if (!args_info->gtpnl_given) return 0;
- if (gtp_dev_create(GTP_DEVNAME, args_info->gtpnl_orig, + if (gtp_dev_create(ns, GTP_DEVNAME, args_info->gtpnl_orig, gsn->fd0, gsn->fd1u) < 0) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "cannot create GTP tunnel device: %s\n", strerror(errno)); return -1; } + gtp_nl.enabled = true; + gtp_nl.ns = ns;
gtp_nl.nl = genl_socket_open(); if (gtp_nl.nl == NULL) { @@ -127,6 +136,12 @@ int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, DEBUGP(DGGSN, "Setting route to reach %s via %s\n", args_info->net_arg, GTP_DEVNAME);
+ /* configure gtp dev in it's own namespace */ + if (ns > 0) + switch_ns(ns, &oldmask); + + gtp_nl.ifidx = if_nametoindex(GTP_DEVNAME); + if (gtp_dev_config(GTP_DEVNAME, net, mask2prefix(mask)) < 0) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Cannot add route to reach network %s\n", @@ -151,12 +166,16 @@ int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, if (err < 0) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "Failed to launch script `%s'", ipup); - return -1; + ret = -1; } } + + if (ns > 0) + restore_ns(&oldmask); + SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
- return 0; + return ret; }
void gtp_kernel_stop(void) @@ -185,7 +204,8 @@ int gtp_kernel_tunnel_add(struct pdp_t *pdp) memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr)); memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
- gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME)); + gtp_tunnel_set_ifns(t, gtp_nl.ns); + gtp_tunnel_set_ifidx(t, gtp_nl.ifidx); gtp_tunnel_set_version(t, pdp->version); gtp_tunnel_set_ms_ip4(t, &ms); gtp_tunnel_set_sgsn_ip4(t, &sgsn); @@ -216,7 +236,8 @@ int gtp_kernel_tunnel_del(struct pdp_t *pdp) if (t == NULL) return -1;
- gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME)); + gtp_tunnel_set_ifns(t, gtp_nl.ns); + gtp_tunnel_set_ifidx(t, gtp_nl.ifidx); gtp_tunnel_set_version(t, pdp->version); if (pdp->version == 0) { gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi)); diff --git a/ggsn/gtp-kernel.h b/ggsn/gtp-kernel.h index 628002f..ce636a9 100644 --- a/ggsn/gtp-kernel.h +++ b/ggsn/gtp-kernel.h @@ -7,7 +7,8 @@ extern int debug; extern char *ipup;
#ifdef GTP_KERNEL -int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, +int gtp_kernel_init(int ns, + struct gsn_t *gsn, struct in_addr *net, struct in_addr *mask, struct gengetopt_args_info *args_info); void gtp_kernel_stop(void); @@ -18,7 +19,8 @@ int gtp_kernel_tunnel_del(struct pdp_t *pdp); int gtp_kernel_enabled(void);
#else -static inline int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net, +static inline int gtp_kernel_init(int ns, + struct gsn_t *gsn, struct in_addr *net, struct in_addr *mask, struct gengetopt_args_info *args_info) { diff --git a/lib/Makefile.am b/lib/Makefile.am index 756d566..54c8953 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,7 +1,7 @@ noinst_LIBRARIES = libmisc.a
-noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h +noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h netns.h
AM_CFLAGS = -O2 -fno-builtin -Wall -DSBINDIR='"$(sbindir)"' -ggdb $(LIBOSMOCORE_CFLAGS)
-libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c +libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c netns.c diff --git a/lib/netns.c b/lib/netns.c new file mode 100644 index 0000000..29f2536 --- /dev/null +++ b/lib/netns.c @@ -0,0 +1,135 @@ +/* + * NETNS functions. + * Copyright (C) 2014, 2015 Travelping GmbH + * + * The contents of this file may be used under the terms of the GNU + * General Public License Version 2, provided that the above copyright + * notice and this permission notice is included in all copies or + * substantial portions of the software. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sched.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/mount.h> +#include <sys/param.h> +#include <fcntl.h> +#include <errno.h> + +#include "netns.h" + +#define NETNS_PATH "/var/run/netns" + +int default_nsfd; + +int switch_ns(int nsfd, sigset_t *oldmask) +{ + sigset_t intmask; + + sigfillset(&intmask); + sigprocmask(SIG_BLOCK, &intmask, oldmask); + + return setns(nsfd, CLONE_NEWNET); +} + +void restore_ns(sigset_t *oldmask) +{ + setns(default_nsfd, CLONE_NEWNET); + + sigprocmask(SIG_SETMASK, oldmask, NULL); +} + +int open_ns(int nsfd, const char *pathname, int flags) +{ + sigset_t intmask, oldmask; + int fd; + int errsv; + + sigfillset(&intmask); + sigprocmask(SIG_BLOCK, &intmask, &oldmask); + + setns(nsfd, CLONE_NEWNET); + fd = open(pathname, flags); + errsv = errno; + setns(default_nsfd, CLONE_NEWNET); + + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + errno = errsv; + return fd; +} + +int socket_ns(int nsfd, int domain, int type, int protocol) +{ + sigset_t intmask, oldmask; + int sk; + int errsv; + + sigfillset(&intmask); + sigprocmask(SIG_BLOCK, &intmask, &oldmask); + + setns(nsfd, CLONE_NEWNET); + sk = socket(domain, type, protocol); + errsv = errno; + setns(default_nsfd, CLONE_NEWNET); + + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + errno = errsv; + return sk; +} + +void init_netns() +{ + if ((default_nsfd = open("/proc/self/ns/net", O_RDONLY)) < 0) { + perror("init_netns"); + exit(EXIT_FAILURE); + } +} + +int get_nsfd(const char *name) +{ + int r; + sigset_t intmask, oldmask; + char path[MAXPATHLEN] = NETNS_PATH; + + r = mkdir(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); + if (r < 0 && errno != EEXIST) + return r; + + snprintf(path, sizeof(path), "%s/%s", NETNS_PATH, name); + r = open(path, O_RDONLY|O_CREAT|O_EXCL, 0); + if (r < 0) { + if (errno == EEXIST) + return open(path, O_RDONLY); + + return r; + } + close(r); + + sigfillset(&intmask); + sigprocmask(SIG_BLOCK, &intmask, &oldmask); + + unshare(CLONE_NEWNET); + mount("/proc/self/ns/net", path, "none", MS_BIND, NULL); + + setns(default_nsfd, CLONE_NEWNET); + + sigprocmask(SIG_SETMASK, &oldmask, NULL); + + return open(path, O_RDONLY); +} diff --git a/lib/netns.h b/lib/netns.h new file mode 100644 index 0000000..1b646c9 --- /dev/null +++ b/lib/netns.h @@ -0,0 +1,26 @@ +/* + * NETNS functions. + * Copyright (C) 2014, 2015 Travelping GmbH + * + * The contents of this file may be used under the terms of the GNU + * General Public License Version 2, provided that the above copyright + * notice and this permission notice is included in all copies or + * substantial portions of the software. + * + */ + +#ifndef __NETNS_H +#define __NETNS_H + +extern int default_nsfd; + +void init_netns(void); + +int switch_ns(int nsfd, sigset_t *oldmask); +void restore_ns(sigset_t *oldmask); + +int open_ns(int nsfd, const char *pathname, int flags); +int socket_ns(int nsfd, int domain, int type, int protocol); +int get_nsfd(const char *name); + +#endif
On Tue, Nov 17, 2015 at 12:22:45PM +0100, Andreas Schultz wrote:
The kernel gtp can now be create in an existing sub namespace.
Could you please document this a bit more?
BTW, if I'm asking for things it is not because I want to start some philosophical debate on documentation and submission standards...
The reason behind is way more simple than all that: the less time I have to dedicate to follow your track, the less time I have to dedicate on this sort of hobby project.
Thanks.
On 17 Nov 2015, at 12:22, Andreas Schultz aschultz@tpip.net wrote:
- Copyright (C) 2014, 2015 Travelping GmbH
 
- The contents of this file may be used under the terms of the GNU
 
- General Public License Version 2, provided that the above copyright
 
- notice and this permission notice is included in all copies or
 
- substantial portions of the software.
 
Do you think you could license this GPLv2 or later? Even if most of OpenGGSN is currently stuck at V2 it would be nice if new code doesn't need to be replaced.
+int open_ns(int nsfd, const char *pathname, int flags) +{
- sigset_t intmask, oldmask;
 - int fd;
 - int errsv;
 - sigfillset(&intmask);
 - sigprocmask(SIG_BLOCK, &intmask, &oldmask);
 - setns(nsfd, CLONE_NEWNET);
 - fd = open(pathname, flags);
 - errsv = errno;
 - setns(default_nsfd, CLONE_NEWNET);
 - sigprocmask(SIG_SETMASK, &oldmask, NULL);
 - errno = errsv;
 - return fd;
 
Do you want to add error handling here?
+#ifndef __NETNS_H +#define __NETNS_H
__ is reserved for system headers/system code. We should not use it. #pragma once solves the issue in a very nice way for us.