ipnetns.c revision 33724939097b8ebb5c37cc0dc2b5e57fe342c8fe
1#define _ATFILE_SOURCE 2#include <sys/types.h> 3#include <sys/stat.h> 4#include <sys/wait.h> 5#include <sys/inotify.h> 6#include <sys/mount.h> 7#include <sys/param.h> 8#include <sys/syscall.h> 9#include <stdio.h> 10#include <string.h> 11#include <sched.h> 12#include <fcntl.h> 13#include <dirent.h> 14#include <errno.h> 15#include <unistd.h> 16#include <ctype.h> 17 18#include "utils.h" 19#include "ip_common.h" 20#include "namespace.h" 21 22static int usage(void) 23{ 24 fprintf(stderr, "Usage: ip netns list\n"); 25 fprintf(stderr, " ip netns add NAME\n"); 26 fprintf(stderr, " ip [-all] netns delete [NAME]\n"); 27 fprintf(stderr, " ip netns identify [PID]\n"); 28 fprintf(stderr, " ip netns pids NAME\n"); 29 fprintf(stderr, " ip [-all] netns exec [NAME] cmd ...\n"); 30 fprintf(stderr, " ip netns monitor\n"); 31 exit(-1); 32} 33 34static int netns_list(int argc, char **argv) 35{ 36 struct dirent *entry; 37 DIR *dir; 38 39 dir = opendir(NETNS_RUN_DIR); 40 if (!dir) 41 return 0; 42 43 while ((entry = readdir(dir)) != NULL) { 44 if (strcmp(entry->d_name, ".") == 0) 45 continue; 46 if (strcmp(entry->d_name, "..") == 0) 47 continue; 48 printf("%s\n", entry->d_name); 49 } 50 closedir(dir); 51 return 0; 52} 53 54static int cmd_exec(const char *cmd, char **argv, bool do_fork) 55{ 56 fflush(stdout); 57 if (do_fork) { 58 int status; 59 pid_t pid; 60 61 pid = fork(); 62 if (pid < 0) { 63 perror("fork"); 64 exit(1); 65 } 66 67 if (pid != 0) { 68 /* Parent */ 69 if (waitpid(pid, &status, 0) < 0) { 70 perror("waitpid"); 71 exit(1); 72 } 73 74 if (WIFEXITED(status)) { 75 return WEXITSTATUS(status); 76 } 77 78 exit(1); 79 } 80 } 81 82 if (execvp(cmd, argv) < 0) 83 fprintf(stderr, "exec of \"%s\" failed: %s\n", 84 cmd, strerror(errno)); 85 _exit(1); 86} 87 88static int on_netns_exec(char *nsname, void *arg) 89{ 90 char **argv = arg; 91 cmd_exec(argv[1], argv + 1, true); 92 return 0; 93} 94 95static int netns_exec(int argc, char **argv) 96{ 97 /* Setup the proper environment for apps that are not netns 98 * aware, and execute a program in that environment. 99 */ 100 const char *cmd; 101 102 if (argc < 1 && !do_all) { 103 fprintf(stderr, "No netns name specified\n"); 104 return -1; 105 } 106 if ((argc < 2 && !do_all) || (argc < 1 && do_all)) { 107 fprintf(stderr, "No command specified\n"); 108 return -1; 109 } 110 111 if (do_all) 112 return do_each_netns(on_netns_exec, --argv, 1); 113 114 if (netns_switch(argv[0])) 115 return -1; 116 117 /* ip must return the status of the child, 118 * but do_cmd() will add a minus to this, 119 * so let's add another one here to cancel it. 120 */ 121 cmd = argv[1]; 122 return -cmd_exec(cmd, argv + 1, !!batch_mode); 123} 124 125static int is_pid(const char *str) 126{ 127 int ch; 128 for (; (ch = *str); str++) { 129 if (!isdigit(ch)) 130 return 0; 131 } 132 return 1; 133} 134 135static int netns_pids(int argc, char **argv) 136{ 137 const char *name; 138 char net_path[MAXPATHLEN]; 139 int netns; 140 struct stat netst; 141 DIR *dir; 142 struct dirent *entry; 143 144 if (argc < 1) { 145 fprintf(stderr, "No netns name specified\n"); 146 return -1; 147 } 148 if (argc > 1) { 149 fprintf(stderr, "extra arguments specified\n"); 150 return -1; 151 } 152 153 name = argv[0]; 154 snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name); 155 netns = open(net_path, O_RDONLY); 156 if (netns < 0) { 157 fprintf(stderr, "Cannot open network namespace: %s\n", 158 strerror(errno)); 159 return -1; 160 } 161 if (fstat(netns, &netst) < 0) { 162 fprintf(stderr, "Stat of netns failed: %s\n", 163 strerror(errno)); 164 return -1; 165 } 166 dir = opendir("/proc/"); 167 if (!dir) { 168 fprintf(stderr, "Open of /proc failed: %s\n", 169 strerror(errno)); 170 return -1; 171 } 172 while((entry = readdir(dir))) { 173 char pid_net_path[MAXPATHLEN]; 174 struct stat st; 175 if (!is_pid(entry->d_name)) 176 continue; 177 snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%s/ns/net", 178 entry->d_name); 179 if (stat(pid_net_path, &st) != 0) 180 continue; 181 if ((st.st_dev == netst.st_dev) && 182 (st.st_ino == netst.st_ino)) { 183 printf("%s\n", entry->d_name); 184 } 185 } 186 closedir(dir); 187 return 0; 188 189} 190 191static int netns_identify(int argc, char **argv) 192{ 193 const char *pidstr; 194 char net_path[MAXPATHLEN]; 195 int netns; 196 struct stat netst; 197 DIR *dir; 198 struct dirent *entry; 199 200 if (argc < 1) { 201 pidstr = "self"; 202 } else if (argc > 1) { 203 fprintf(stderr, "extra arguments specified\n"); 204 return -1; 205 } else { 206 pidstr = argv[0]; 207 if (!is_pid(pidstr)) { 208 fprintf(stderr, "Specified string '%s' is not a pid\n", 209 pidstr); 210 return -1; 211 } 212 } 213 214 snprintf(net_path, sizeof(net_path), "/proc/%s/ns/net", pidstr); 215 netns = open(net_path, O_RDONLY); 216 if (netns < 0) { 217 fprintf(stderr, "Cannot open network namespace: %s\n", 218 strerror(errno)); 219 return -1; 220 } 221 if (fstat(netns, &netst) < 0) { 222 fprintf(stderr, "Stat of netns failed: %s\n", 223 strerror(errno)); 224 return -1; 225 } 226 dir = opendir(NETNS_RUN_DIR); 227 if (!dir) { 228 /* Succeed treat a missing directory as an empty directory */ 229 if (errno == ENOENT) 230 return 0; 231 232 fprintf(stderr, "Failed to open directory %s:%s\n", 233 NETNS_RUN_DIR, strerror(errno)); 234 return -1; 235 } 236 237 while((entry = readdir(dir))) { 238 char name_path[MAXPATHLEN]; 239 struct stat st; 240 241 if (strcmp(entry->d_name, ".") == 0) 242 continue; 243 if (strcmp(entry->d_name, "..") == 0) 244 continue; 245 246 snprintf(name_path, sizeof(name_path), "%s/%s", NETNS_RUN_DIR, 247 entry->d_name); 248 249 if (stat(name_path, &st) != 0) 250 continue; 251 252 if ((st.st_dev == netst.st_dev) && 253 (st.st_ino == netst.st_ino)) { 254 printf("%s\n", entry->d_name); 255 } 256 } 257 closedir(dir); 258 return 0; 259 260} 261 262static int on_netns_del(char *nsname, void *arg) 263{ 264 char netns_path[MAXPATHLEN]; 265 266 snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, nsname); 267 umount2(netns_path, MNT_DETACH); 268 if (unlink(netns_path) < 0) { 269 fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n", 270 netns_path, strerror(errno)); 271 return -1; 272 } 273 return 0; 274} 275 276static int netns_delete(int argc, char **argv) 277{ 278 if (argc < 1 && !do_all) { 279 fprintf(stderr, "No netns name specified\n"); 280 return -1; 281 } 282 283 if (do_all) 284 return netns_foreach(on_netns_del, NULL); 285 286 return on_netns_del(argv[0], NULL); 287} 288 289static int create_netns_dir(void) 290{ 291 /* Create the base netns directory if it doesn't exist */ 292 if (mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) { 293 if (errno != EEXIST) { 294 fprintf(stderr, "mkdir %s failed: %s\n", 295 NETNS_RUN_DIR, strerror(errno)); 296 return -1; 297 } 298 } 299 300 return 0; 301} 302 303static int netns_add(int argc, char **argv) 304{ 305 /* This function creates a new network namespace and 306 * a new mount namespace and bind them into a well known 307 * location in the filesystem based on the name provided. 308 * 309 * The mount namespace is created so that any necessary 310 * userspace tweaks like remounting /sys, or bind mounting 311 * a new /etc/resolv.conf can be shared between uers. 312 */ 313 char netns_path[MAXPATHLEN]; 314 const char *name; 315 int fd; 316 int made_netns_run_dir_mount = 0; 317 318 if (argc < 1) { 319 fprintf(stderr, "No netns name specified\n"); 320 return -1; 321 } 322 name = argv[0]; 323 324 snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name); 325 326 if (create_netns_dir()) 327 return -1; 328 329 /* Make it possible for network namespace mounts to propagate between 330 * mount namespaces. This makes it likely that a unmounting a network 331 * namespace file in one namespace will unmount the network namespace 332 * file in all namespaces allowing the network namespace to be freed 333 * sooner. 334 */ 335 while (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) { 336 /* Fail unless we need to make the mount point */ 337 if (errno != EINVAL || made_netns_run_dir_mount) { 338 fprintf(stderr, "mount --make-shared %s failed: %s\n", 339 NETNS_RUN_DIR, strerror(errno)); 340 return -1; 341 } 342 343 /* Upgrade NETNS_RUN_DIR to a mount point */ 344 if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND, NULL)) { 345 fprintf(stderr, "mount --bind %s %s failed: %s\n", 346 NETNS_RUN_DIR, NETNS_RUN_DIR, strerror(errno)); 347 return -1; 348 } 349 made_netns_run_dir_mount = 1; 350 } 351 352 /* Create the filesystem state */ 353 fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0); 354 if (fd < 0) { 355 fprintf(stderr, "Cannot create namespace file \"%s\": %s\n", 356 netns_path, strerror(errno)); 357 return -1; 358 } 359 close(fd); 360 if (unshare(CLONE_NEWNET) < 0) { 361 fprintf(stderr, "Failed to create a new network namespace \"%s\": %s\n", 362 name, strerror(errno)); 363 goto out_delete; 364 } 365 366 /* Bind the netns last so I can watch for it */ 367 if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0) { 368 fprintf(stderr, "Bind /proc/self/ns/net -> %s failed: %s\n", 369 netns_path, strerror(errno)); 370 goto out_delete; 371 } 372 return 0; 373out_delete: 374 netns_delete(argc, argv); 375 return -1; 376} 377 378 379static int netns_monitor(int argc, char **argv) 380{ 381 char buf[4096]; 382 struct inotify_event *event; 383 int fd; 384 fd = inotify_init(); 385 if (fd < 0) { 386 fprintf(stderr, "inotify_init failed: %s\n", 387 strerror(errno)); 388 return -1; 389 } 390 391 if (create_netns_dir()) 392 return -1; 393 394 if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) { 395 fprintf(stderr, "inotify_add_watch failed: %s\n", 396 strerror(errno)); 397 return -1; 398 } 399 for(;;) { 400 ssize_t len = read(fd, buf, sizeof(buf)); 401 if (len < 0) { 402 fprintf(stderr, "read failed: %s\n", 403 strerror(errno)); 404 return -1; 405 } 406 for (event = (struct inotify_event *)buf; 407 (char *)event < &buf[len]; 408 event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len)) { 409 if (event->mask & IN_CREATE) 410 printf("add %s\n", event->name); 411 if (event->mask & IN_DELETE) 412 printf("delete %s\n", event->name); 413 } 414 } 415 return 0; 416} 417 418int do_netns(int argc, char **argv) 419{ 420 if (argc < 1) 421 return netns_list(0, NULL); 422 423 if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) || 424 (matches(*argv, "lst") == 0)) 425 return netns_list(argc-1, argv+1); 426 427 if (matches(*argv, "help") == 0) 428 return usage(); 429 430 if (matches(*argv, "add") == 0) 431 return netns_add(argc-1, argv+1); 432 433 if (matches(*argv, "delete") == 0) 434 return netns_delete(argc-1, argv+1); 435 436 if (matches(*argv, "identify") == 0) 437 return netns_identify(argc-1, argv+1); 438 439 if (matches(*argv, "pids") == 0) 440 return netns_pids(argc-1, argv+1); 441 442 if (matches(*argv, "exec") == 0) 443 return netns_exec(argc-1, argv+1); 444 445 if (matches(*argv, "monitor") == 0) 446 return netns_monitor(argc-1, argv+1); 447 448 fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv); 449 exit(-1); 450} 451