From patchwork Tue Nov 17 11:22:45 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Schultz X-Patchwork-Id: 545526 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.osmocom.org (tmp.osmocom.org [144.76.43.76]) by ozlabs.org (Postfix) with ESMTP id C17A91402D5 for ; Tue, 17 Nov 2015 22:22:57 +1100 (AEDT) Received: from lists.osmocom.org (lists.osmocom.org [144.76.43.76]) by lists.osmocom.org (Postfix) with ESMTP id CCC2A9211; Tue, 17 Nov 2015 11:22:55 +0000 (UTC) X-Original-To: openbsc@lists.osmocom.org Delivered-To: openbsc@lists.osmocom.org Received: from mail.tpip.net (mail.tpip.net [92.43.49.48]) by lists.osmocom.org (Postfix) with ESMTP id 3BC249200 for ; Tue, 17 Nov 2015 11:22:54 +0000 (UTC) Received: from office.tpip.net (office.tpip.net [92.43.51.2]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.tpip.net (Postfix) with ESMTPS id E71774F418; Tue, 17 Nov 2015 11:22:53 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by office.tpip.net (Postfix) with ESMTP id B7661A2CA1; Tue, 17 Nov 2015 12:22:55 +0100 (CET) Received: from office.tpip.net ([127.0.0.1]) by localhost (office.tpip.net [127.0.0.1]) (amavisd-new, port 10032) with ESMTP id QrU-hWzeylZH; Tue, 17 Nov 2015 12:22:54 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by office.tpip.net (Postfix) with ESMTP id B2F96A2CA7; Tue, 17 Nov 2015 12:22:54 +0100 (CET) X-Virus-Scanned: amavisd-new at tpip.net Received: from office.tpip.net ([127.0.0.1]) by localhost (office.tpip.net [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id SjnYIDsjmRSD; Tue, 17 Nov 2015 12:22:54 +0100 (CET) Received: from alice.tpip.org (unknown [192.168.13.53]) by office.tpip.net (Postfix) with ESMTPSA id 5C4BEA2CAD; Tue, 17 Nov 2015 12:22:54 +0100 (CET) From: Andreas Schultz To: openbsc@lists.osmocom.org Subject: [PATCH openggsn 4/4] ggsn: add network namespace support Date: Tue, 17 Nov 2015 12:22:45 +0100 Message-Id: <1447759365-24099-5-git-send-email-aschultz@tpip.net> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1447759365-24099-1-git-send-email-aschultz@tpip.net> References: <1447759365-24099-1-git-send-email-aschultz@tpip.net> X-BeenThere: openbsc@lists.osmocom.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: Development of the OpenBSC GSM base station controller List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Pablo Neira Ayuso Errors-To: openbsc-bounces@lists.osmocom.org Sender: "OpenBSC" The kernel gtp can now be create in an existing sub namespace. Signed-off-by: Andreas Schultz --- 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 +#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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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