diff mbox

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

Message ID 1447759365-24099-5-git-send-email-aschultz@tpip.net
State Superseded
Headers show

Commit Message

Andreas Schultz Nov. 17, 2015, 11:22 a.m. UTC
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

Comments

Pablo Neira Ayuso Nov. 17, 2015, 12:45 p.m. UTC | #1
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.
Holger Freyther Dec. 21, 2015, 7:30 a.m. UTC | #2
> 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.
diff mbox

Patch

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