Change in osmo-ggsn[master]: add Linux network namespace support for TUN device

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/gerrit-log@lists.osmocom.org/.

laforge gerrit-no-reply at lists.osmocom.org
Mon Mar 2 13:32:23 UTC 2020


laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-ggsn/+/17253 )

Change subject: add Linux network namespace support for TUN device
......................................................................

add Linux network namespace support for TUN device

Change-Id: Idd0ad8fa9c8e7ba0aeec1b52947598d4d297b620
---
M lib/Makefile.am
A lib/netns.c
A lib/netns.h
M sgsnemu/cmdline.c
M sgsnemu/cmdline.ggo
M sgsnemu/cmdline.h
M sgsnemu/sgsnemu.c
7 files changed, 294 insertions(+), 12 deletions(-)

Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved
  pespin: Looks good to me, but someone else must approve



diff --git a/lib/Makefile.am b/lib/Makefile.am
index 533d777..f2c5dc9 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,10 +1,10 @@
 noinst_LIBRARIES = libmisc.a
 
-noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h util.h
+noinst_HEADERS = gnugetopt.h ippool.h lookup.h syserr.h tun.h in46_addr.h netdev.h gtp-kernel.h netns.h util.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 in46_addr.c netdev.c util.c
+libmisc_a_SOURCES = getopt1.c getopt.c ippool.c lookup.c tun.c debug.c in46_addr.c netdev.c netns.c util.c
 
 if ENABLE_GTP_KERNEL
 AM_CFLAGS += -DGTP_KERNEL $(LIBGTPNL_CFLAGS)
diff --git a/lib/netns.c b/lib/netns.c
new file mode 100644
index 0000000..6734b5d
--- /dev/null
+++ b/lib/netns.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014-2017, Travelping GmbH <info at travelping.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if defined(__linux__)
+
+#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 <signal.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"
+
+static 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);
+}
+
+#endif
diff --git a/lib/netns.h b/lib/netns.h
new file mode 100644
index 0000000..168e44f
--- /dev/null
+++ b/lib/netns.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014-2017, Travelping GmbH <info at travelping.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __NETNS_H
+#define __NETNS_H
+
+#if defined(__linux__)
+
+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
+
+#endif
diff --git a/sgsnemu/cmdline.c b/sgsnemu/cmdline.c
index b062533..13d0295 100644
--- a/sgsnemu/cmdline.c
+++ b/sgsnemu/cmdline.c
@@ -72,6 +72,7 @@
 	"      --ipup=STRING             Script to run after link-up",
 	"      --ipdown=STRING           Script to run after link-down",
 	"      --tun-device=STRING       Name of the local network interface",
+	"      --netns=STRING            Network namespace to use",
 	"\n Mode: pinghost\n  generate ICMP payload inside G-PDU without setting up tun interface",
 	"      --pinghost=STRING         Ping remote host",
 	"      --pingrate=INT            Number of ping req per second  (default=`1')",
@@ -163,6 +164,7 @@
 	args_info->ipup_given = 0;
 	args_info->ipdown_given = 0;
 	args_info->tun_device_given = 0;
+	args_info->netns_given = 0;
 	args_info->pinghost_given = 0;
 	args_info->pingrate_given = 0;
 	args_info->pingsize_given = 0;
@@ -244,6 +246,8 @@
 	args_info->ipdown_orig = NULL;
 	args_info->tun_device_arg = NULL;
 	args_info->tun_device_orig = NULL;
+	args_info->netns_arg = NULL;
+	args_info->netns_orig = NULL;
 	args_info->pinghost_arg = NULL;
 	args_info->pinghost_orig = NULL;
 	args_info->pingrate_arg = 1;
@@ -300,13 +304,14 @@
 	args_info->ipup_help = gengetopt_args_info_help[35];
 	args_info->ipdown_help = gengetopt_args_info_help[36];
 	args_info->tun_device_help = gengetopt_args_info_help[37];
-	args_info->pinghost_help = gengetopt_args_info_help[39];
-	args_info->pingrate_help = gengetopt_args_info_help[40];
-	args_info->pingsize_help = gengetopt_args_info_help[41];
-	args_info->pingcount_help = gengetopt_args_info_help[42];
-	args_info->pingquiet_help = gengetopt_args_info_help[43];
-	args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[44];
-	args_info->pdp_type_help = gengetopt_args_info_help[45];
+	args_info->netns_help = gengetopt_args_info_help[38];
+	args_info->pinghost_help = gengetopt_args_info_help[40];
+	args_info->pingrate_help = gengetopt_args_info_help[41];
+	args_info->pingsize_help = gengetopt_args_info_help[42];
+	args_info->pingcount_help = gengetopt_args_info_help[43];
+	args_info->pingquiet_help = gengetopt_args_info_help[44];
+	args_info->no_tx_gpdu_seq_help = gengetopt_args_info_help[45];
+	args_info->pdp_type_help = gengetopt_args_info_help[46];
 
 }
 
@@ -432,6 +437,8 @@
 	free_string_field(&(args_info->ipdown_orig));
 	free_string_field(&(args_info->tun_device_arg));
 	free_string_field(&(args_info->tun_device_orig));
+	free_string_field(&(args_info->netns_arg));
+	free_string_field(&(args_info->netns_orig));
 	free_string_field(&(args_info->pinghost_arg));
 	free_string_field(&(args_info->pinghost_orig));
 	free_string_field(&(args_info->pingrate_orig));
@@ -545,6 +552,8 @@
 	if (args_info->tun_device_given)
 		write_into_file(outfile, "tun-device",
 				args_info->tun_device_orig, 0);
+	if (args_info->netns_given)
+		write_into_file(outfile, "netns", args_info->netns_orig, 0);
 	if (args_info->pinghost_given)
 		write_into_file(outfile, "pinghost", args_info->pinghost_orig,
 				0);
@@ -709,6 +718,12 @@
 			prog_name, (additional_error ? additional_error : ""));
 		error_occurred = 1;
 	}
+	if (args_info->netns_given && !args_info->createif_given) {
+		fprintf(stderr,
+			"%s: '--netns' option depends on option 'createif'%s\n",
+			prog_name, (additional_error ? additional_error : ""));
+		error_occurred = 1;
+	}
 	if (args_info->pingrate_given && !args_info->pinghost_given) {
 		fprintf(stderr,
 			"%s: '--pingrate' option depends on option 'pinghost'%s\n",
@@ -954,6 +969,7 @@
 			{"ipup", 1, NULL, 0},
 			{"ipdown", 1, NULL, 0},
 			{"tun-device", 1, NULL, 0},
+			{"netns", 1, NULL, 0},
 			{"pinghost", 1, NULL, 0},
 			{"pingrate", 1, NULL, 0},
 			{"pingsize", 1, NULL, 0},
@@ -1494,6 +1510,22 @@
 					goto failure;
 
 			}
+			/* Network namespace to use.  */
+			else if (strcmp
+				 (long_options[option_index].name,
+				  "netns") == 0) {
+				args_info->createif_mode_counter += 1;
+
+				if (update_arg((void *)&(args_info->netns_arg),
+					       &(args_info->netns_orig),
+					       &(args_info->netns_given),
+					       &(local_args_info.netns_given),
+					       optarg, 0, 0, ARG_STRING,
+					       check_ambiguity, override, 0, 0,
+					       "netns", '-', additional_error))
+					goto failure;
+
+			}
 			/* Ping remote host.  */
 			else if (strcmp
 				 (long_options[option_index].name,
@@ -1609,11 +1641,12 @@
 		int createif_given[] =
 		    { args_info->createif_given, args_info->net_given,
 			args_info->defaultroute_given, args_info->ipup_given,
-			args_info->ipdown_given, args_info->tun_device_given, -1
+			args_info->ipdown_given, args_info->tun_device_given,
+			args_info->netns_given, -1
 		};
 		const char *createif_desc[] =
 		    { "--createif", "--net", "--defaultroute", "--ipup",
-			"--ipdown", "--tun-device", 0
+			"--ipdown", "--tun-device", "--netns", 0
 		};
 		int pinghost_given[] =
 		    { args_info->pinghost_given, args_info->pingrate_given,
diff --git a/sgsnemu/cmdline.ggo b/sgsnemu/cmdline.ggo
index 0f415f5..0d074aa 100644
--- a/sgsnemu/cmdline.ggo
+++ b/sgsnemu/cmdline.ggo
@@ -59,6 +59,7 @@
 modeoption   "ipup"         - "Script to run after link-up"    string dependon="createif" no        mode="createif"
 modeoption   "ipdown"       - "Script to run after link-down"  string dependon="createif" no        mode="createif"
 modeoption   "tun-device"   - "Name of the local network interface" string dependon="createif" no   mode="createif"
+modeoption   "netns"        - "Network namespace to use"       string dependon="createif" no        mode="createif"
 
 modeoption   "pinghost"     - "Ping remote host"               string no                                mode="pinghost"
 modeoption   "pingrate"     - "Number of ping req per second"  int default="1" dependon="pinghost" no   mode="pinghost"
diff --git a/sgsnemu/cmdline.h b/sgsnemu/cmdline.h
index 24f772b..c7c8521 100644
--- a/sgsnemu/cmdline.h
+++ b/sgsnemu/cmdline.h
@@ -242,6 +242,12 @@
 				/**< @brief Name of the local network interface original value given at command line.  */
 		const char *tun_device_help;
 			       /**< @brief Name of the local network interface help description.  */
+		char *netns_arg;
+			/**< @brief Network namespace to use.  */
+		char *netns_orig;
+			/**< @brief Network namespace to use original value given at command line.  */
+		const char *netns_help;
+			  /**< @brief Network namespace to use help description.  */
 		char *pinghost_arg;
 			/**< @brief Ping remote host.  */
 		char *pinghost_orig;
@@ -355,6 +361,8 @@
 				/**< @brief Whether ipdown was given.  */
 		unsigned int tun_device_given;
 					/**< @brief Whether tun-device was given.  */
+		unsigned int netns_given;
+				/**< @brief Whether netns was given.  */
 		unsigned int pinghost_given;
 				/**< @brief Whether pinghost was given.  */
 		unsigned int pingrate_given;
diff --git a/sgsnemu/sgsnemu.c b/sgsnemu/sgsnemu.c
index 7904c49..2c0ce1b 100644
--- a/sgsnemu/sgsnemu.c
+++ b/sgsnemu/sgsnemu.c
@@ -50,6 +50,7 @@
 #include "../lib/tun.h"
 #include "../lib/ippool.h"
 #include "../lib/syserr.h"
+#include "../lib/netns.h"
 #include "../gtp/pdp.h"
 #include "../gtp/gtp.h"
 #include "cmdline.h"
@@ -81,12 +82,16 @@
 int maxfd = 0;			/* For select() */
 int echoversion = 1;		/* First try this version */
 void *tall_sgsnemu_ctx;		/* root talloc ctx */
+#if defined(__linux__)
+int netns = -1;			/* network namespace */
+#endif
 
 /* Struct with local versions of gengetopt options */
 struct {
 	int debug;		/* Print debug messages */
 	int createif;		/* Create local network interface */
 	char *tun_dev_name;
+	char *netns;
 	struct in46_addr netaddr, destaddr, net;	/* Network interface  */
 	size_t prefixlen;
 	char *ipup, *ipdown;	/* Filename of scripts */
@@ -294,6 +299,8 @@
 		printf("createif: %d\n", args_info.createif_flag);
 		if (args_info.tun_device_arg)
 			printf("tun-device: %s\n", args_info.tun_device_arg);
+		if (args_info.netns_arg)
+			printf("netns: %s\n", args_info.netns_arg);
 		if (args_info.ipup_arg)
 			printf("ipup: %s\n", args_info.ipup_arg);
 		if (args_info.ipdown_arg)
@@ -352,6 +359,8 @@
 			printf("createif: %d\n", args_info.createif_flag);
 			if (args_info.tun_device_arg)
 				printf("tun-device: %s\n", args_info.tun_device_arg);
+			if (args_info.netns_arg)
+				printf("netns: %s\n", args_info.netns_arg);
 			if (args_info.ipup_arg)
 				printf("ipup: %s\n", args_info.ipup_arg);
 			if (args_info.ipdown_arg)
@@ -870,6 +879,7 @@
 	/* createif */
 	options.createif = args_info.createif_flag;
 	options.tun_dev_name = args_info.tun_device_arg;
+	options.netns = args_info.netns_arg;
 
 	/* net                                                          */
 	/* Store net as in_addr net and mask                            */
@@ -1313,10 +1323,23 @@
 
 static int delete_context(struct pdp_t *pdp)
 {
+	if (tun && options.ipdown) {
+#if defined(__linux__)
+		sigset_t oldmask;
 
-	if (tun && options.ipdown)
+		if ((options.netns)) {
+			switch_ns(netns, &oldmask);
+		}
+#endif
 		tun_runscript(tun, options.ipdown);
 
+#if defined(__linux__)
+		if ((options.netns)) {
+			restore_ns(&oldmask);
+		}
+#endif
+	}
+
 	ipdel((struct iphash_t *)pdp->peer[0]);
 	memset(pdp->peer[0], 0, sizeof(struct iphash_t));	/* To be sure */
 
@@ -1377,6 +1400,9 @@
 static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
 {
 	struct in46_addr addr;
+#if defined(__linux__)
+	sigset_t oldmask;
+#endif
 
 	struct iphash_t *iph = (struct iphash_t *)cbp;
 
@@ -1430,6 +1456,12 @@
 		break;
 	}
 
+#if defined(__linux__)
+	if ((options.createif) && (options.netns)) {
+		switch_ns(netns, &oldmask);
+	}
+#endif
+
 	if ((options.createif) && (!options.net.len)) {
 		size_t prefixlen = 32;
 		if (addr.len == 16)
@@ -1470,6 +1502,12 @@
 		free(forwarding);
 	}
 
+#if defined(__linux__)
+	if ((options.createif) && (options.netns)) {
+		restore_ns(&oldmask);
+	}
+#endif
+
 	ipset(iph, &addr);
 
 	state = 2;		/* Connected */
@@ -1543,6 +1581,9 @@
 	struct timezone tz;	/* Used for calculating ping times */
 	struct timeval tv;
 	int diff;
+#if defined(__linux__)
+	sigset_t oldmask;
+#endif
 
 	signal(SIGTERM, signal_handler);
 	signal(SIGHUP,  signal_handler);
@@ -1552,6 +1593,10 @@
 	msgb_talloc_ctx_init(tall_sgsnemu_ctx, 0);
 	osmo_init_logging2(tall_sgsnemu_ctx, &log_info);
 
+#if defined(__linux__)
+	init_netns();
+#endif
+
 	/* Process options given in configuration file and command line */
 	if (process_options(argc, argv))
 		exit(1);
@@ -1575,6 +1620,13 @@
 	else
 		gtp_set_cb_data_ind(gsn, encaps_ping);
 
+#if defined(__linux__)
+	if ((options.createif) && (options.netns)) {
+		netns = get_nsfd(options.netns);
+		switch_ns(netns, &oldmask);
+	}
+#endif
+
 	if (options.createif) {
 		printf("Setting up interface\n");
 		/* Create a tunnel interface */
@@ -1600,6 +1652,12 @@
 			tun_runscript(tun, options.ipup);
 	}
 
+#if defined(__linux__)
+	if ((options.createif) && (options.netns)) {
+		restore_ns(&oldmask);
+	}
+#endif
+
 	/* Initialise hash tables */
 	memset(&iphash, 0, sizeof(iphash));
 	memset(&iparr, 0, sizeof(iparr));

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-ggsn/+/17253
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ggsn
Gerrit-Branch: master
Gerrit-Change-Id: Idd0ad8fa9c8e7ba0aeec1b52947598d4d297b620
Gerrit-Change-Number: 17253
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Assignee: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200302/6a516e9c/attachment.htm>


More information about the gerrit-log mailing list