Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 1 | /* |
| 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 Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 47 | /*! default namespace of the GGSN process */ |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 48 | static int default_nsfd; |
| 49 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 50 | /*! 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 Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 54 | int switch_ns(int nsfd, sigset_t *oldmask) |
| 55 | { |
| 56 | sigset_t intmask; |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 57 | int rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 58 | |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 59 | if (sigfillset(&intmask) < 0) |
| 60 | return -errno; |
| 61 | if ((rc = sigprocmask(SIG_BLOCK, &intmask, oldmask)) != 0) |
| 62 | return -rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 63 | |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 64 | if (setns(nsfd, CLONE_NEWNET) < 0) |
| 65 | return -errno; |
| 66 | return 0; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 67 | } |
| 68 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 69 | /*! 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 Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 72 | int restore_ns(sigset_t *oldmask) |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 73 | { |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 74 | int rc; |
| 75 | if (setns(default_nsfd, CLONE_NEWNET) < 0) |
| 76 | return -errno; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 77 | |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 78 | if ((rc = sigprocmask(SIG_SETMASK, oldmask, NULL)) != 0) |
| 79 | return -rc; |
| 80 | return 0; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 81 | } |
| 82 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 83 | /*! open a file from within specified network namespace */ |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 84 | int open_ns(int nsfd, const char *pathname, int flags) |
| 85 | { |
| 86 | sigset_t intmask, oldmask; |
| 87 | int fd; |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 88 | int rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 89 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 90 | /* mask off all signals, store old signal mask */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 91 | if (sigfillset(&intmask) < 0) |
| 92 | return -errno; |
| 93 | if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0) |
| 94 | return -rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 95 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 96 | /* associate the calling thread with namespace file descriptor */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 97 | if (setns(nsfd, CLONE_NEWNET) < 0) |
| 98 | return -errno; |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 99 | /* open the requested file/path */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 100 | if ((fd = open(pathname, flags)) < 0) |
| 101 | return -errno; |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 102 | /* return back to default namespace */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 103 | if (setns(default_nsfd, CLONE_NEWNET) < 0) { |
| 104 | close(fd); |
| 105 | return -errno; |
| 106 | } |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 107 | /* restore process mask */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 108 | if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) { |
| 109 | close(fd); |
| 110 | return -rc; |
| 111 | } |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 112 | |
Vadim Yanitskiy | 20539f0 | 2020-03-03 15:37:42 +0700 | [diff] [blame] | 113 | return fd; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 114 | } |
| 115 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 116 | /*! 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 Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 124 | int socket_ns(int nsfd, int domain, int type, int protocol) |
| 125 | { |
| 126 | sigset_t intmask, oldmask; |
| 127 | int sk; |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 128 | int rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 129 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 130 | /* mask off all signals, store old signal mask */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 131 | if (sigfillset(&intmask) < 0) |
| 132 | return -errno; |
| 133 | if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0) |
| 134 | return -rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 135 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 136 | /* associate the calling thread with namespace file descriptor */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 137 | if (setns(nsfd, CLONE_NEWNET) < 0) |
| 138 | return -errno; |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 139 | |
| 140 | /* create socket of requested domain/type/proto */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 141 | if ((sk = socket(domain, type, protocol)) < 0) |
| 142 | return -errno; |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 143 | |
| 144 | /* return back to default namespace */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 145 | if (setns(default_nsfd, CLONE_NEWNET) < 0) { |
| 146 | close(sk); |
| 147 | return -errno; |
| 148 | } |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 149 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 150 | /* restore process mask */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 151 | if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) { |
| 152 | close(sk); |
| 153 | return -rc; |
| 154 | } |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 155 | return sk; |
| 156 | } |
| 157 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 158 | /*! 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 Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 161 | int init_netns() |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 162 | { |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 163 | /* store the default namespace for later reference */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 164 | if ((default_nsfd = open("/proc/self/ns/net", O_RDONLY)) < 0) |
| 165 | return -errno; |
| 166 | return 0; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 167 | } |
| 168 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 169 | /*! 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 Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 173 | int get_nsfd(const char *name) |
| 174 | { |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 175 | int rc; |
| 176 | int fd; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 177 | sigset_t intmask, oldmask; |
| 178 | char path[MAXPATHLEN] = NETNS_PATH; |
| 179 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 180 | /* create /var/run/netns, if it doesn't exist already */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 181 | rc = mkdir(path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); |
| 182 | if (rc < 0 && errno != EEXIST) |
| 183 | return rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 184 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 185 | /* create /var/run/netns/[name], if it doesn't exist already */ |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 186 | snprintf(path, sizeof(path), "%s/%s", NETNS_PATH, name); |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 187 | 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 Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 195 | } |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 196 | if (close(fd) < 0) |
| 197 | return -errno; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 198 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 199 | /* mask off all signals, store old signal mask */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 200 | if (sigfillset(&intmask) < 0) |
| 201 | return -errno; |
| 202 | if ((rc = sigprocmask(SIG_BLOCK, &intmask, &oldmask)) != 0) |
| 203 | return -rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 204 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 205 | /* create a new network namespace */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 206 | 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 Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 210 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 211 | /* switch back to default namespace */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 212 | if (setns(default_nsfd, CLONE_NEWNET) < 0) |
| 213 | return -errno; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 214 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 215 | /* restore process mask */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 216 | if ((rc = sigprocmask(SIG_SETMASK, &oldmask, NULL)) != 0) |
| 217 | return -rc; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 218 | |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 219 | /* finally, open the created namespace file descriptor from default ns */ |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 220 | if ((fd = open(path, O_RDONLY)) < 0) |
| 221 | return -errno; |
Harald Welte | 20d9d15 | 2020-04-11 11:55:53 +0200 | [diff] [blame^] | 222 | |
Pau Espin Pedrol | ad6eaa2 | 2020-02-25 12:17:29 +0100 | [diff] [blame] | 223 | return fd; |
Andreas Schultz | b629240 | 2018-10-05 13:58:45 +0100 | [diff] [blame] | 224 | } |
| 225 | |
| 226 | #endif |