blob: 1e77a045af58b4849cfb78dc516e7138cc127b72 [file] [log] [blame]
Andreas Schultzb6292402018-10-05 13:58:45 +01001/*
2 * Copyright (C) 2014-2017, Travelping GmbH <info@travelping.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as
6 * published by the Free Software Foundation, either version 3 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19#if defined(__linux__)
20
21#ifdef HAVE_CONFIG_H
22# include "config.h"
23#endif
24
25#ifndef _GNU_SOURCE
26# define _GNU_SOURCE
27#endif
28
29#include <errno.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33#include <sched.h>
34#include <signal.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <sys/socket.h>
38#include <sys/mount.h>
39#include <sys/param.h>
40#include <fcntl.h>
41#include <errno.h>
42
43#include "netns.h"
44
45#define NETNS_PATH "/var/run/netns"
46
Harald Welte20d9d152020-04-11 11:55:53 +020047/*! default namespace of the GGSN process */
Andreas Schultzb6292402018-10-05 13:58:45 +010048static int default_nsfd;
49
Harald Welte20d9d152020-04-11 11:55:53 +020050/*! switch to a (non-default) namespace, store existing signal mask in oldmask.
51 * \param[in] nsfd file descriptor representing the namespace to whch we shall switch
52 * \param[out] oldmask caller-provided memory location to which old signal mask is stored
53 * \ returns 0 on success or negative (errno) in case of error */
Andreas Schultzb6292402018-10-05 13:58:45 +010054int switch_ns(int nsfd, sigset_t *oldmask)
55{
56 sigset_t intmask;
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010057 int rc;
Andreas Schultzb6292402018-10-05 13:58:45 +010058
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010059 if (sigfillset(&intmask) < 0)
60 return -errno;
61 if ((rc = sigprocmask(SIG_BLOCK, &intmask, oldmask)) != 0)
62 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +010063
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010064 if (setns(nsfd, CLONE_NEWNET) < 0)
65 return -errno;
66 return 0;
Andreas Schultzb6292402018-10-05 13:58:45 +010067}
68
Harald Welte20d9d152020-04-11 11:55:53 +020069/*! switch back to the default namespace, restoring signal mask.
70 * \param[in] oldmask signal mask to restore after returning to default namespace
71 * \returns 0 on successs; negative errno value in case of error */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010072int restore_ns(sigset_t *oldmask)
Andreas Schultzb6292402018-10-05 13:58:45 +010073{
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010074 int rc;
75 if (setns(default_nsfd, CLONE_NEWNET) < 0)
76 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +010077
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010078 if ((rc = sigprocmask(SIG_SETMASK, oldmask, NULL)) != 0)
79 return -rc;
80 return 0;
Andreas Schultzb6292402018-10-05 13:58:45 +010081}
82
Harald Welte20d9d152020-04-11 11:55:53 +020083/*! open a file from within specified network namespace */
Andreas Schultzb6292402018-10-05 13:58:45 +010084int open_ns(int nsfd, const char *pathname, int flags)
85{
86 sigset_t intmask, oldmask;
87 int fd;
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010088 int rc;
Andreas Schultzb6292402018-10-05 13:58:45 +010089
Harald Welte20d9d152020-04-11 11:55:53 +020090 /* mask off all signals, store old signal mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010091 if (sigfillset(&intmask) < 0)
92 return -errno;
93 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
94 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +010095
Harald Welte20d9d152020-04-11 11:55:53 +020096 /* associate the calling thread with namespace file descriptor */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +010097 if (setns(nsfd, CLONE_NEWNET) < 0)
98 return -errno;
Harald Welte20d9d152020-04-11 11:55:53 +020099 /* open the requested file/path */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100100 if ((fd = open(pathname, flags)) < 0)
101 return -errno;
Harald Welte20d9d152020-04-11 11:55:53 +0200102 /* return back to default namespace */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100103 if (setns(default_nsfd, CLONE_NEWNET) < 0) {
104 close(fd);
105 return -errno;
106 }
Harald Welte20d9d152020-04-11 11:55:53 +0200107 /* restore process mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100108 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
109 close(fd);
110 return -rc;
111 }
Andreas Schultzb6292402018-10-05 13:58:45 +0100112
Vadim Yanitskiy20539f02020-03-03 15:37:42 +0700113 return fd;
Andreas Schultzb6292402018-10-05 13:58:45 +0100114}
115
Harald Welte20d9d152020-04-11 11:55:53 +0200116/*! create a socket in another namespace.
117 * Switches temporarily to namespace indicated by nsfd, creates a socket in
118 * that namespace and then returns to the default namespace.
119 * \param[in] nsfd File descriptor of the namspace in which to create socket
120 * \param[in] domain Domain of the socket (AF_INET, ...)
121 * \param[in] type Type of the socket (SOCK_STREAM, ...)
122 * \param[in] protocol Protocol of the socket (IPPROTO_TCP, ...)
123 * \returns 0 on success; negative errno in case of error */
Andreas Schultzb6292402018-10-05 13:58:45 +0100124int socket_ns(int nsfd, int domain, int type, int protocol)
125{
126 sigset_t intmask, oldmask;
127 int sk;
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100128 int rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100129
Harald Welte20d9d152020-04-11 11:55:53 +0200130 /* mask off all signals, store old signal mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100131 if (sigfillset(&intmask) < 0)
132 return -errno;
133 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
134 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100135
Harald Welte20d9d152020-04-11 11:55:53 +0200136 /* associate the calling thread with namespace file descriptor */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100137 if (setns(nsfd, CLONE_NEWNET) < 0)
138 return -errno;
Harald Welte20d9d152020-04-11 11:55:53 +0200139
140 /* create socket of requested domain/type/proto */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100141 if ((sk = socket(domain, type, protocol)) < 0)
142 return -errno;
Harald Welte20d9d152020-04-11 11:55:53 +0200143
144 /* return back to default namespace */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100145 if (setns(default_nsfd, CLONE_NEWNET) < 0) {
146 close(sk);
147 return -errno;
148 }
Andreas Schultzb6292402018-10-05 13:58:45 +0100149
Harald Welte20d9d152020-04-11 11:55:53 +0200150 /* restore process mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100151 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
152 close(sk);
153 return -rc;
154 }
Andreas Schultzb6292402018-10-05 13:58:45 +0100155 return sk;
156}
157
Harald Welte20d9d152020-04-11 11:55:53 +0200158/*! initialize this network namespace helper module.
159 * Must be called before using any other functions of this file.
160 * \returns 0 on success; negative errno in case of error */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100161int init_netns()
Andreas Schultzb6292402018-10-05 13:58:45 +0100162{
Harald Welte20d9d152020-04-11 11:55:53 +0200163 /* store the default namespace for later reference */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100164 if ((default_nsfd = open("/proc/self/ns/net", O_RDONLY)) < 0)
165 return -errno;
166 return 0;
Andreas Schultzb6292402018-10-05 13:58:45 +0100167}
168
Harald Welte20d9d152020-04-11 11:55:53 +0200169/*! create obtain file descriptor for network namespace of give name.
170 * Creates /var/run/netns if it doesn't exist already.
171 * \param[in] name Name of the network namespace (in /var/run/netns/)
172 * \returns File descriptor of network namespace; negative errno in case of error */
Andreas Schultzb6292402018-10-05 13:58:45 +0100173int get_nsfd(const char *name)
174{
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100175 int rc;
176 int fd;
Andreas Schultzb6292402018-10-05 13:58:45 +0100177 sigset_t intmask, oldmask;
178 char path[MAXPATHLEN] = NETNS_PATH;
179
Harald Welte20d9d152020-04-11 11:55:53 +0200180 /* create /var/run/netns, if it doesn't exist already */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100181 rc = mkdir(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
182 if (rc < 0 && errno != EEXIST)
183 return rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100184
Harald Welte20d9d152020-04-11 11:55:53 +0200185 /* create /var/run/netns/[name], if it doesn't exist already */
Andreas Schultzb6292402018-10-05 13:58:45 +0100186 snprintf(path, sizeof(path), "%s/%s", NETNS_PATH, name);
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100187 fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
188 if (fd < 0) {
189 if (errno == EEXIST) {
190 if ((fd = open(path, O_RDONLY)) < 0)
191 return -errno;
192 return fd;
193 }
194 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100195 }
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100196 if (close(fd) < 0)
197 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100198
Harald Welte20d9d152020-04-11 11:55:53 +0200199 /* mask off all signals, store old signal mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100200 if (sigfillset(&intmask) < 0)
201 return -errno;
202 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
203 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100204
Harald Welte20d9d152020-04-11 11:55:53 +0200205 /* create a new network namespace */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100206 if (unshare(CLONE_NEWNET) < 0)
207 return -errno;
208 if (mount("/proc/self/ns/net", path, "none", MS_BIND, NULL) < 0)
209 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100210
Harald Welte20d9d152020-04-11 11:55:53 +0200211 /* switch back to default namespace */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100212 if (setns(default_nsfd, CLONE_NEWNET) < 0)
213 return -errno;
Andreas Schultzb6292402018-10-05 13:58:45 +0100214
Harald Welte20d9d152020-04-11 11:55:53 +0200215 /* restore process mask */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100216 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0)
217 return -rc;
Andreas Schultzb6292402018-10-05 13:58:45 +0100218
Harald Welte20d9d152020-04-11 11:55:53 +0200219 /* finally, open the created namespace file descriptor from default ns */
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100220 if ((fd = open(path, O_RDONLY)) < 0)
221 return -errno;
Harald Welte20d9d152020-04-11 11:55:53 +0200222
Pau Espin Pedrolad6eaa22020-02-25 12:17:29 +0100223 return fd;
Andreas Schultzb6292402018-10-05 13:58:45 +0100224}
225
226#endif