blob: b0ec254f9a3cdb61698539f2e11636120722c2c2 [file] [log] [blame]
Harald Weltef7365592020-04-15 21:48:45 +02001#warning "Merge netns.c from osmo-ggsn and osmo-gtpu-daemon"
2/*
3 * Copyright (C) 2014-2017, Travelping GmbH <info@travelping.com>
4 * Copyright (C) 2020, Harald Welte <laforge@gnumonks.org>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21#if defined(__linux__)
22
23#ifdef HAVE_CONFIG_H
24# include "config.h"
25#endif
26
27#ifndef _GNU_SOURCE
28# define _GNU_SOURCE
29#endif
30
31#include <errno.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <sched.h>
36#include <signal.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <sys/socket.h>
40#include <sys/mount.h>
41#include <sys/param.h>
42#include <fcntl.h>
43#include <errno.h>
44
45#include <osmocom/core/utils.h>
46
47#include "netns.h"
48
49#define NETNS_PATH "/var/run/netns"
50
51/*! default namespace of the GGSN process */
52static int default_nsfd = -1;
53
54/*! switch to a (non-default) namespace, store existing signal mask in oldmask.
55 * \param[in] nsfd file descriptor representing the namespace to whch we shall switch
56 * \param[out] oldmaks caller-provided memory location to which old signal mask is stored
57 * \ returns 0 on success or negative (errno) in case of error */
58int switch_ns(int nsfd, sigset_t *oldmask)
59{
60 sigset_t intmask;
61 int rc;
62
63 OSMO_ASSERT(default_nsfd >= 0);
64
65 if (sigfillset(&intmask) < 0)
66 return -errno;
67 if ((rc = sigprocmask(SIG_BLOCK, &intmask, oldmask)) != 0)
68 return -rc;
69
70 if (setns(nsfd, CLONE_NEWNET) < 0) {
71 /* restore old mask if we couldn't switch the netns */
72 sigprocmask(SIG_SETMASK, oldmask, NULL);
73 return -errno;
74 }
75 return 0;
76}
77
78/*! switch back to the default namespace, restoring signal mask.
79 * \param[in] oldmask signal mask to restore after returning to default namespace
80 * \returns 0 on successs; negative errno value in case of error */
81int restore_ns(sigset_t *oldmask)
82{
83 OSMO_ASSERT(default_nsfd >= 0);
84
85 int rc;
86 if (setns(default_nsfd, CLONE_NEWNET) < 0)
87 return -errno;
88
89 if ((rc = sigprocmask(SIG_SETMASK, oldmask, NULL)) != 0)
90 return -rc;
91 return 0;
92}
93
94/*! open a file from within specified network namespace */
95int open_ns(int nsfd, const char *pathname, int flags)
96{
97 sigset_t intmask, oldmask;
98 int ret;
99 int fd = -1;
100 int rc;
101
102 OSMO_ASSERT(default_nsfd >= 0);
103
104 /* mask off all signals, store old signal mask */
105 if (sigfillset(&intmask) < 0)
106 return -errno;
107 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
108 return -rc;
109
110 /* associate the calling thread with namespace file descriptor */
111 if (setns(nsfd, CLONE_NEWNET) < 0) {
112 ret = -errno;
113 goto restore_sigmask;
114 }
115 /* open the requested file/path */
116 if ((fd = open(pathname, flags)) < 0) {
117 ret = -errno;
118 goto restore_defaultns;
119 }
120 ret = fd;
121
122restore_defaultns:
123 /* return back to default namespace */
124 if (setns(default_nsfd, CLONE_NEWNET) < 0) {
125 if (fd >= 0)
126 close(fd);
127 return -errno;
128 }
129
130restore_sigmask:
131 /* restore process mask */
132 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
133 if (fd >= 0)
134 close(fd);
135 return -rc;
136 }
137
138 return ret;
139}
140
141/*! create a socket in another namespace.
142 * Switches temporarily to namespace indicated by nsfd, creates a socket in
143 * that namespace and then returns to the default namespace.
144 * \param[in] nsfd File descriptor of the namspace in which to create socket
145 * \param[in] domain Domain of the socket (AF_INET, ...)
146 * \param[in] type Type of the socket (SOCK_STREAM, ...)
147 * \param[in] protocol Protocol of the socket (IPPROTO_TCP, ...)
148 * \returns 0 on success; negative errno in case of error */
149int socket_ns(int nsfd, int domain, int type, int protocol)
150{
151 sigset_t intmask, oldmask;
152 int ret;
153 int sk = -1;
154 int rc;
155
156 OSMO_ASSERT(default_nsfd >= 0);
157
158 /* mask off all signals, store old signal mask */
159 if (sigfillset(&intmask) < 0)
160 return -errno;
161 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
162 return -rc;
163
164 /* associate the calling thread with namespace file descriptor */
165 if (setns(nsfd, CLONE_NEWNET) < 0) {
166 ret = -errno;
167 goto restore_sigmask;
168 }
169
170 /* create socket of requested domain/type/proto */
171 if ((sk = socket(domain, type, protocol)) < 0) {
172 ret = -errno;
173 goto restore_defaultns;
174 }
175 ret = sk;
176
177restore_defaultns:
178 /* return back to default namespace */
179 if (setns(default_nsfd, CLONE_NEWNET) < 0) {
180 if (sk >= 0)
181 close(sk);
182 return -errno;
183 }
184
185restore_sigmask:
186 /* restore process mask */
187 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) {
188 if (sk >= 0)
189 close(sk);
190 return -rc;
191 }
192 return ret;
193}
194
195/*! initialize this network namespace helper module.
196 * Must be called before using any other functions of this file.
197 * \returns 0 on success; negative errno in case of error */
198int init_netns()
199{
200 /* store the default namespace for later reference */
201 if ((default_nsfd = open("/proc/self/ns/net", O_RDONLY)) < 0)
202 return -errno;
203 return 0;
204}
205
206/*! create obtain file descriptor for network namespace of give name.
207 * Creates /var/run/netns if it doesn't exist already.
208 * \param[in] name Name of the network namespace (in /var/run/netns/)
209 * \returns File descriptor of network namespace; negative errno in case of error */
210int get_nsfd(const char *name)
211{
212 int ret = 0;
213 int rc;
214 int fd;
215 sigset_t intmask, oldmask;
216 char path[MAXPATHLEN] = NETNS_PATH;
217
218 OSMO_ASSERT(default_nsfd >= 0);
219
220 /* create /var/run/netns, if it doesn't exist already */
221 rc = mkdir(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
222 if (rc < 0 && errno != EEXIST)
223 return rc;
224
225 /* create /var/run/netns/[name], if it doesn't exist already */
226 snprintf(path, sizeof(path), "%s/%s", NETNS_PATH, name);
227 fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
228 if (fd < 0) {
229 if (errno == EEXIST) {
230 if ((fd = open(path, O_RDONLY)) < 0)
231 return -errno;
232 return fd;
233 }
234 return -errno;
235 }
236 if (close(fd) < 0)
237 return -errno;
238
239 /* mask off all signals, store old signal mask */
240 if (sigfillset(&intmask) < 0)
241 return -errno;
242 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
243 return -rc;
244
245 /* create a new network namespace */
246 if (unshare(CLONE_NEWNET) < 0) {
247 ret = -errno;
248 goto restore_sigmask;
249 }
250 if (mount("/proc/self/ns/net", path, "none", MS_BIND, NULL) < 0)
251 ret = -errno;
252
253 /* switch back to default namespace */
254 if (setns(default_nsfd, CLONE_NEWNET) < 0)
255 return -errno;
256
257restore_sigmask:
258 /* restore process mask */
259 if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0)
260 return -rc;
261
262 /* might have been set above in case mount fails */
263 if (ret < 0)
264 return ret;
265
266 /* finally, open the created namespace file descriptor from default ns */
267 if ((fd = open(path, O_RDONLY)) < 0)
268 return -errno;
269
270 return fd;
271}
272
273#endif