blob: b16a42b556e4ae597a7e3d54494ec0ceec9e0672 [file] [log] [blame]
Andreas Schultzb6292402018-10-05 13:58:45 +01001/*
2 * Copyright (C) 2014-2017, Travelping GmbH <info@travelping.com>
Harald Welte8398bcc2020-04-11 12:08:17 +02003 * Copyright (C) 2020, Harald Welte <laforge@gnumonks.org>
Andreas Schultzb6292402018-10-05 13:58:45 +01004 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20#if defined(__linux__)
21
22#ifdef HAVE_CONFIG_H
23# include "config.h"
24#endif
25
26#ifndef _GNU_SOURCE
27# define _GNU_SOURCE
28#endif
29
30#include <errno.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <sched.h>
35#include <signal.h>
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <sys/socket.h>
39#include <sys/mount.h>
40#include <sys/param.h>
41#include <fcntl.h>
42#include <errno.h>
43
Harald Welte61b010c2020-04-11 12:14:56 +020044#include <osmocom/core/utils.h>
45
Andreas Schultzb6292402018-10-05 13:58:45 +010046#include "netns.h"
47
48#define NETNS_PATH "/var/run/netns"
49
Harald Welte20d9d152020-04-11 11:55:53 +020050/*! default namespace of the GGSN process */
Harald Welte61b010c2020-04-11 12:14:56 +020051static int default_nsfd = -1;
Andreas Schultzb6292402018-10-05 13:58:45 +010052
Harald Welte20d9d152020-04-11 11:55:53 +020053/*! switch to a (non-default) namespace, store existing signal mask in oldmask.
54 * \param[in] nsfd file descriptor representing the namespace to whch we shall switch
55 * \param[out] oldmask caller-provided memory location to which old signal mask is stored
56 * \ returns 0 on success or negative (errno) in case of error */
Andreas Schultzb6292402018-10-05 13:58:45 +010057int switch_ns(int nsfd, sigset_t *oldmask)
58{
59 sigset_t intmask;
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010060 int rc;
Andreas Schultzb6292402018-10-05 13:58:45 +010061
Harald Welte61b010c2020-04-11 12:14:56 +020062 OSMO_ASSERT(default_nsfd >= 0);
63
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010064 if (sigfillset(&intmask) < 0)
65 return -errno;
66 if ((rc = sigprocmask(SIG_BLOCK, &intmask, oldmask)) != 0)
67 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +010068
Harald Welte8398bcc2020-04-11 12:08:17 +020069 if (setns(nsfd, CLONE_NEWNET) < 0) {
70 /* restore old mask if we couldn't switch the netns */
71 sigprocmask(SIG_SETMASK, oldmask, NULL);
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010072 return -errno;
Harald Welte8398bcc2020-04-11 12:08:17 +020073 }
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010074 return 0;
Andreas Schultzb6292402018-10-05 13:58:45 +010075}
76
Harald Welte20d9d152020-04-11 11:55:53 +020077/*! switch back to the default namespace, restoring signal mask.
78 * \param[in] oldmask signal mask to restore after returning to default namespace
79 * \returns 0 on successs; negative errno value in case of error */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010080int restore_ns(sigset_t *oldmask)
Andreas Schultzb6292402018-10-05 13:58:45 +010081{
Harald Welte61b010c2020-04-11 12:14:56 +020082 OSMO_ASSERT(default_nsfd >= 0);
83
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010084 int rc;
85 if (setns(default_nsfd, CLONE_NEWNET) < 0)
86 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +010087
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010088 if ((rc = sigprocmask(SIG_SETMASK, oldmask, NULL)) != 0)
89 return -rc;
90 return 0;
Andreas Schultzb6292402018-10-05 13:58:45 +010091}
92
Harald Welte20d9d152020-04-11 11:55:53 +020093/*! open a file from within specified network namespace */
Andreas Schultzb6292402018-10-05 13:58:45 +010094int open_ns(int nsfd, const char *pathname, int flags)
95{
96 sigset_t intmask, oldmask;
Harald Welte8398bcc2020-04-11 12:08:17 +020097 int ret;
98 int fd = -1;
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010099 int rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100100
Harald Welte61b010c2020-04-11 12:14:56 +0200101 OSMO_ASSERT(default_nsfd >= 0);
102
Harald Welte20d9d152020-04-11 11:55:53 +0200103 /* mask off all signals, store old signal mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100104 if (sigfillset(&intmask) < 0)
105 return -errno;
106 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
107 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100108
Harald Welte20d9d152020-04-11 11:55:53 +0200109 /* associate the calling thread with namespace file descriptor */
Harald Welte8398bcc2020-04-11 12:08:17 +0200110 if (setns(nsfd, CLONE_NEWNET) < 0) {
111 ret = -errno;
112 goto restore_sigmask;
113 }
Harald Welte20d9d152020-04-11 11:55:53 +0200114 /* open the requested file/path */
Harald Welte8398bcc2020-04-11 12:08:17 +0200115 if ((fd = open(pathname, flags)) < 0) {
116 ret = -errno;
117 goto restore_defaultns;
118 }
119 ret = fd;
120
121restore_defaultns:
Harald Welte20d9d152020-04-11 11:55:53 +0200122 /* return back to default namespace */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100123 if (setns(default_nsfd, CLONE_NEWNET) < 0) {
Harald Welte8398bcc2020-04-11 12:08:17 +0200124 if (fd >= 0)
125 close(fd);
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100126 return -errno;
127 }
Harald Welte8398bcc2020-04-11 12:08:17 +0200128
129restore_sigmask:
Harald Welte20d9d152020-04-11 11:55:53 +0200130 /* restore process mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100131 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
Harald Welte8398bcc2020-04-11 12:08:17 +0200132 if (fd >= 0)
133 close(fd);
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100134 return -rc;
135 }
Andreas Schultzb6292402018-10-05 13:58:45 +0100136
Harald Welte8398bcc2020-04-11 12:08:17 +0200137 return ret;
Andreas Schultzb6292402018-10-05 13:58:45 +0100138}
139
Harald Welte20d9d152020-04-11 11:55:53 +0200140/*! create a socket in another namespace.
141 * Switches temporarily to namespace indicated by nsfd, creates a socket in
142 * that namespace and then returns to the default namespace.
143 * \param[in] nsfd File descriptor of the namspace in which to create socket
144 * \param[in] domain Domain of the socket (AF_INET, ...)
145 * \param[in] type Type of the socket (SOCK_STREAM, ...)
146 * \param[in] protocol Protocol of the socket (IPPROTO_TCP, ...)
147 * \returns 0 on success; negative errno in case of error */
Andreas Schultzb6292402018-10-05 13:58:45 +0100148int socket_ns(int nsfd, int domain, int type, int protocol)
149{
150 sigset_t intmask, oldmask;
Harald Welte8398bcc2020-04-11 12:08:17 +0200151 int ret;
152 int sk = -1;
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100153 int rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100154
Harald Welte61b010c2020-04-11 12:14:56 +0200155 OSMO_ASSERT(default_nsfd >= 0);
156
Harald Welte20d9d152020-04-11 11:55:53 +0200157 /* mask off all signals, store old signal mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100158 if (sigfillset(&intmask) < 0)
159 return -errno;
160 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
161 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100162
Harald Welte20d9d152020-04-11 11:55:53 +0200163 /* associate the calling thread with namespace file descriptor */
Harald Welte8398bcc2020-04-11 12:08:17 +0200164 if (setns(nsfd, CLONE_NEWNET) < 0) {
165 ret = -errno;
166 goto restore_sigmask;
167 }
Harald Welte20d9d152020-04-11 11:55:53 +0200168
169 /* create socket of requested domain/type/proto */
Harald Welte8398bcc2020-04-11 12:08:17 +0200170 if ((sk = socket(domain, type, protocol)) < 0) {
171 ret = -errno;
172 goto restore_defaultns;
173 }
174 ret = sk;
Harald Welte20d9d152020-04-11 11:55:53 +0200175
Harald Welte8398bcc2020-04-11 12:08:17 +0200176restore_defaultns:
Harald Welte20d9d152020-04-11 11:55:53 +0200177 /* return back to default namespace */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100178 if (setns(default_nsfd, CLONE_NEWNET) < 0) {
Harald Welte8398bcc2020-04-11 12:08:17 +0200179 if (sk >= 0)
180 close(sk);
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100181 return -errno;
182 }
Andreas Schultzb6292402018-10-05 13:58:45 +0100183
Harald Welte8398bcc2020-04-11 12:08:17 +0200184restore_sigmask:
Harald Welte20d9d152020-04-11 11:55:53 +0200185 /* restore process mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100186 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
Harald Welte8398bcc2020-04-11 12:08:17 +0200187 if (sk >= 0)
188 close(sk);
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100189 return -rc;
190 }
Harald Welte8398bcc2020-04-11 12:08:17 +0200191 return ret;
Andreas Schultzb6292402018-10-05 13:58:45 +0100192}
193
Harald Welte20d9d152020-04-11 11:55:53 +0200194/*! initialize this network namespace helper module.
195 * Must be called before using any other functions of this file.
196 * \returns 0 on success; negative errno in case of error */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100197int init_netns()
Andreas Schultzb6292402018-10-05 13:58:45 +0100198{
Harald Welte20d9d152020-04-11 11:55:53 +0200199 /* store the default namespace for later reference */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100200 if ((default_nsfd = open("/proc/self/ns/net", O_RDONLY)) < 0)
201 return -errno;
202 return 0;
Andreas Schultzb6292402018-10-05 13:58:45 +0100203}
204
Harald Welte20d9d152020-04-11 11:55:53 +0200205/*! create obtain file descriptor for network namespace of give name.
206 * Creates /var/run/netns if it doesn't exist already.
207 * \param[in] name Name of the network namespace (in /var/run/netns/)
208 * \returns File descriptor of network namespace; negative errno in case of error */
Andreas Schultzb6292402018-10-05 13:58:45 +0100209int get_nsfd(const char *name)
210{
Harald Welte8398bcc2020-04-11 12:08:17 +0200211 int ret = 0;
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100212 int rc;
213 int fd;
Andreas Schultzb6292402018-10-05 13:58:45 +0100214 sigset_t intmask, oldmask;
215 char path[MAXPATHLEN] = NETNS_PATH;
216
Harald Welte61b010c2020-04-11 12:14:56 +0200217 OSMO_ASSERT(default_nsfd >= 0);
218
Harald Welte20d9d152020-04-11 11:55:53 +0200219 /* create /var/run/netns, if it doesn't exist already */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100220 rc = mkdir(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
221 if (rc < 0 && errno != EEXIST)
222 return rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100223
Harald Welte20d9d152020-04-11 11:55:53 +0200224 /* create /var/run/netns/[name], if it doesn't exist already */
Andreas Schultzb6292402018-10-05 13:58:45 +0100225 snprintf(path, sizeof(path), "%s/%s", NETNS_PATH, name);
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100226 fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
227 if (fd < 0) {
228 if (errno == EEXIST) {
229 if ((fd = open(path, O_RDONLY)) < 0)
230 return -errno;
231 return fd;
232 }
233 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100234 }
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100235 if (close(fd) < 0)
236 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100237
Harald Welte20d9d152020-04-11 11:55:53 +0200238 /* mask off all signals, store old signal mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100239 if (sigfillset(&intmask) < 0)
240 return -errno;
241 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
242 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100243
Harald Welte20d9d152020-04-11 11:55:53 +0200244 /* create a new network namespace */
Harald Welte8398bcc2020-04-11 12:08:17 +0200245 if (unshare(CLONE_NEWNET) < 0) {
246 ret = -errno;
247 goto restore_sigmask;
248 }
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100249 if (mount("/proc/self/ns/net", path, "none", MS_BIND, NULL) < 0)
Harald Welte8398bcc2020-04-11 12:08:17 +0200250 ret = -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100251
Harald Welte20d9d152020-04-11 11:55:53 +0200252 /* switch back to default namespace */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100253 if (setns(default_nsfd, CLONE_NEWNET) < 0)
254 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100255
Harald Welte8398bcc2020-04-11 12:08:17 +0200256restore_sigmask:
Harald Welte20d9d152020-04-11 11:55:53 +0200257 /* restore process mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100258 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0)
259 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100260
Harald Welte8398bcc2020-04-11 12:08:17 +0200261 /* might have been set above in case mount fails */
262 if (ret < 0)
263 return ret;
264
Harald Welte20d9d152020-04-11 11:55:53 +0200265 /* finally, open the created namespace file descriptor from default ns */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100266 if ((fd = open(path, O_RDONLY)) < 0)
267 return -errno;
Harald Welte20d9d152020-04-11 11:55:53 +0200268
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100269 return fd;
Andreas Schultzb6292402018-10-05 13:58:45 +0100270}
271
272#endif