blob: c1d75b1ea48a6f7a9ade5336fee2b834d9c1e4c1 [file] [log] [blame]
Pau Espin Pedrol8a5014b2023-01-19 16:54:47 +01001
2/* Network namespace convenience functions
Vadim Yanitskiy03590fc2023-05-18 17:22:26 +07003 * (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
Pau Espin Pedrol8a5014b2023-01-19 16:54:47 +01004 *
5 * All Rights Reserved
6 *
7 * SPDX-License-Identifier: GPL-2.0+
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 */
20
21#include "config.h"
22
23/*! \addtogroup netns
24 * @{
25 * Network namespace convenience functions
26 *
27 * \file netns.c */
28
29#if defined(__linux__)
30
31#ifndef _GNU_SOURCE
32#define _GNU_SOURCE
33#endif
34
35#include <errno.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include <sched.h>
40#include <signal.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <sys/socket.h>
44#include <sys/mount.h>
45#include <sys/param.h>
46#include <fcntl.h>
47#include <errno.h>
48
49#include <osmocom/core/utils.h>
50#include <osmocom/core/netns.h>
51
52#define NETNS_PREFIX_PATH "/var/run/netns"
53#define NETNS_CURRENT_PATH "/proc/self/ns/net"
54
55/*! Open a file descriptor for the current network namespace.
56 * \returns fd of the current network namespace on success; negative in case of error
57 */
58static int netns_open_current_fd(void)
59{
60 int fd;
61 /* store the default namespace for later reference */
62 if ((fd = open(NETNS_CURRENT_PATH, O_RDONLY)) < 0)
63 return -errno;
64 return fd;
65}
66
67/*! switch to a (non-default) namespace, store existing signal mask in oldmask.
68 * \param[in] nsfd file descriptor representing the namespace to which we shall switch
69 * \param[out] state caller-provided memory location to which state of previous netns is stored
70 * \returns 0 on success; negative on error */
71int osmo_netns_switch_enter(int nsfd, struct osmo_netns_switch_state *state)
72{
73 sigset_t intmask;
74 int rc;
75
76 state->prev_nsfd = -1;
77
78 if (sigfillset(&intmask) < 0)
79 return -errno;
80 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &state->prev_sigmask)) != 0)
81 return -rc;
82 state->prev_nsfd = netns_open_current_fd();
83
84 if (setns(nsfd, CLONE_NEWNET) < 0) {
85 /* restore old mask if we couldn't switch the netns */
86 sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL);
87 close(state->prev_nsfd);
88 state->prev_nsfd = -1;
89 return -errno;
90 }
91 return 0;
92}
93
94/*! switch back to the previous namespace, restoring signal mask.
95 * \param[in] state information about previous netns, filled by osmo_netns_switch_enter()
96 * \returns 0 on successs; negative on error */
97int osmo_netns_switch_exit(struct osmo_netns_switch_state *state)
98{
99 if (state->prev_nsfd < 0)
100 return -EINVAL;
101
102 int rc;
103 if (setns(state->prev_nsfd, CLONE_NEWNET) < 0)
104 return -errno;
105
106 close(state->prev_nsfd);
107 state->prev_nsfd = -1;
108
109 if ((rc = sigprocmask(SIG_SETMASK, &state->prev_sigmask, NULL)) != 0)
110 return -rc;
111 return 0;
112}
113
114static int create_netns(const char *name)
115{
116 char path[MAXPATHLEN];
117 sigset_t intmask, oldmask;
118 int fd, prev_nsfd;
119 int rc, rc2;
120
121 /* create /var/run/netns, if it doesn't exist already */
122 rc = mkdir(NETNS_PREFIX_PATH, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
123 if (rc < 0 && errno != EEXIST)
124 return rc;
125
126 /* create /var/run/netns/[name], if it doesn't exist already */
127 rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name);
128 if (rc >= sizeof(path))
129 return -ENAMETOOLONG;
130 fd = open(path, O_RDONLY|O_CREAT|O_EXCL, 0);
131 if (fd < 0)
132 return -errno;
133 if (close(fd) < 0)
134 return -errno;
135
136 /* mask off all signals, store old signal mask */
137 if (sigfillset(&intmask) < 0)
138 return -errno;
139 if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0)
140 return -rc;
141
142 prev_nsfd = netns_open_current_fd();
143 if (prev_nsfd < 0)
144 return prev_nsfd;
145
146 /* create a new network namespace */
147 if (unshare(CLONE_NEWNET) < 0) {
148 rc = -errno;
149 goto restore_sigmask;
150 }
151 if (mount(NETNS_CURRENT_PATH, path, "none", MS_BIND, NULL) < 0) {
152 rc = -errno;
153 goto restore_sigmask;
154 }
155
156 /* switch back to previous namespace */
157 if (setns(prev_nsfd, CLONE_NEWNET) < 0) {
158 rc = -errno;
159 goto restore_sigmask;
160 }
161
162restore_sigmask:
163 close(prev_nsfd);
164
165 /* restore process mask */
166 if ((rc2 = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0)
167 return -rc2;
168
169 /* might have been set above in case mount fails */
170 if (rc < 0)
171 return rc;
172
173 /* finally, open the created namespace file descriptor from previous ns */
174 if ((fd = open(path, O_RDONLY)) < 0)
175 return -errno;
176
177 return fd;
178}
179
180/*! Open a file descriptor for the network namespace with provided name.
181 * Creates /var/run/netns/ directory if it doesn't exist already.
182 * \param[in] name Name of the network namespace (in /var/run/netns/)
183 * \returns File descriptor of network namespace; negative in case of error
184 */
185int osmo_netns_open_fd(const char *name)
186{
187 int rc;
188 int fd;
189 char path[MAXPATHLEN];
190
191 /* path = /var/run/netns/[name] */
192 rc = snprintf(path, sizeof(path), "%s/%s", NETNS_PREFIX_PATH, name);
193 if (rc >= sizeof(path))
194 return -ENAMETOOLONG;
195
196 /* If netns already exists, simply open it: */
197 fd = open(path, O_RDONLY);
198 if (fd >= 0)
199 return fd;
200
201 /* The netns doesn't exist yet, let's create it: */
202 fd = create_netns(name);
203 return fd;
204}
205
206#endif /* defined(__linux__) */
207
208/*! @} */