[PATCH openggsn 4/4] ggsn: add network namespace support

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/OpenBSC@lists.osmocom.org/.

Andreas Schultz aschultz at tpip.net
Tue Nov 17 11:22:45 UTC 2015


The kernel gtp can now be create in an existing sub namespace.

Signed-off-by: Andreas Schultz <aschultz at 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
-- 
2.5.0




More information about the OpenBSC mailing list