add Linux network namespace support for TUN device
Change-Id: Idd0ad8fa9c8e7ba0aeec1b52947598d4d297b620
This commit is contained in:
parent
b283c32027
commit
b629240a35
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -72,6 +72,7 @@ const char *gengetopt_args_info_help[] = {
|
|||
" --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 @@ void clear_given(struct gengetopt_args_info *args_info)
|
|||
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 @@ void clear_args(struct gengetopt_args_info *args_info)
|
|||
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 @@ void init_args_info(struct gengetopt_args_info *args_info)
|
|||
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 @@ static void cmdline_parser_release(struct gengetopt_args_info *args_info)
|
|||
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 @@ int cmdline_parser_dump(FILE * outfile, struct gengetopt_args_info *args_info)
|
|||
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 @@ cmdline_parser_required2(struct gengetopt_args_info *args_info,
|
|||
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 @@ cmdline_parser_internal(int argc, char **argv,
|
|||
{"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},
|
||||
|
@ -1493,6 +1509,22 @@ cmdline_parser_internal(int argc, char **argv,
|
|||
additional_error))
|
||||
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
|
||||
|
@ -1609,11 +1641,12 @@ cmdline_parser_internal(int argc, char **argv,
|
|||
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,
|
||||
|
|
|
@ -59,6 +59,7 @@ modeoption "defaultroute" - "Create default route" flag dependon="
|
|||
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"
|
||||
|
|
|
@ -242,6 +242,12 @@ extern "C" {
|
|||
/**< @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 @@ extern "C" {
|
|||
/**< @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;
|
||||
|
|
|
@ -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 @@ struct tun_t *tun = NULL; /* TUN instance */
|
|||
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 @@ static int process_options(int argc, char **argv)
|
|||
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 @@ static int process_options(int argc, char **argv)
|
|||
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 @@ static int process_options(int argc, char **argv)
|
|||
/* 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 create_ping(void *gsn, struct pdp_t *pdp,
|
|||
|
||||
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 cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
|
|||
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 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
|||
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 @@ static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause)
|
|||
free(forwarding);
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if ((options.createif) && (options.netns)) {
|
||||
restore_ns(&oldmask);
|
||||
}
|
||||
#endif
|
||||
|
||||
ipset(iph, &addr);
|
||||
|
||||
state = 2; /* Connected */
|
||||
|
@ -1543,6 +1581,9 @@ int main(int argc, char **argv)
|
|||
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 @@ int main(int argc, char **argv)
|
|||
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 @@ int main(int argc, char **argv)
|
|||
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 @@ int main(int argc, char **argv)
|
|||
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));
|
||||
|
|
Loading…
Reference in New Issue