diff options
author | Andreas Schultz <aschultz@tpip.net> | 2018-10-05 13:58:45 +0100 |
---|---|---|
committer | Pau Espin Pedrol <pespin@sysmocom.de> | 2020-02-26 11:16:06 +0100 |
commit | b629240a352c4455e410c32aec1d67a00e2995db (patch) | |
tree | 55d98127ee54ad9ec1879575c4730817c41271bc /lib | |
parent | b283c32027323c70db8e6c6760f04ed263be037f (diff) |
add Linux network namespace support for TUN device
Change-Id: Idd0ad8fa9c8e7ba0aeec1b52947598d4d297b620
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 4 | ||||
-rw-r--r-- | lib/netns.c | 147 | ||||
-rw-r--r-- | lib/netns.h | 35 |
3 files changed, 184 insertions, 2 deletions
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@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@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 |