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 17 #include "utils.h" 18 #include "ip_common.h" 19 20 #define NETNS_RUN_DIR "/var/run/netns" 21 #define NETNS_ETC_DIR "/etc/netns" 22 23 #ifndef CLONE_NEWNET 24 #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ 25 #endif 26 27 #ifndef MNT_DETACH 28 #define MNT_DETACH 0x00000002 /* Just detach from the tree */ 29 #endif /* MNT_DETACH */ 30 31 #ifndef HAVE_SETNS 32 static int setns(int fd, int nstype) 33 { 34 #ifdef __NR_setns 35 return syscall(__NR_setns, fd, nstype); 36 #else 37 errno = ENOSYS; 38 return -1; 39 #endif 40 } 41 #endif /* HAVE_SETNS */ 42 43 44 static void usage(void) __attribute__((noreturn)); 45 46 static void usage(void) 47 { 48 fprintf(stderr, "Usage: ip netns list\n"); 49 fprintf(stderr, " ip netns add NAME\n"); 50 fprintf(stderr, " ip netns delete NAME\n"); 51 fprintf(stderr, " ip netns exec NAME cmd ...\n"); 52 fprintf(stderr, " ip netns monitor\n"); 53 exit(-1); 54 } 55 56 int get_netns_fd(const char *name) 57 { 58 char pathbuf[MAXPATHLEN]; 59 const char *path, *ptr; 60 61 path = name; 62 ptr = strchr(name, '/'); 63 if (!ptr) { 64 snprintf(pathbuf, sizeof(pathbuf), "%s/%s", 65 NETNS_RUN_DIR, name ); 66 path = pathbuf; 67 } 68 return open(path, O_RDONLY); 69 } 70 71 static int netns_list(int argc, char **argv) 72 { 73 struct dirent *entry; 74 DIR *dir; 75 76 dir = opendir(NETNS_RUN_DIR); 77 if (!dir) 78 return 0; 79 80 while ((entry = readdir(dir)) != NULL) { 81 if (strcmp(entry->d_name, ".") == 0) 82 continue; 83 if (strcmp(entry->d_name, "..") == 0) 84 continue; 85 printf("%s\n", entry->d_name); 86 } 87 closedir(dir); 88 return 0; 89 } 90 91 static void bind_etc(const char *name) 92 { 93 char etc_netns_path[MAXPATHLEN]; 94 char netns_name[MAXPATHLEN]; 95 char etc_name[MAXPATHLEN]; 96 struct dirent *entry; 97 DIR *dir; 98 99 snprintf(etc_netns_path, sizeof(etc_netns_path), "%s/%s", NETNS_ETC_DIR, name); 100 dir = opendir(etc_netns_path); 101 if (!dir) 102 return; 103 104 while ((entry = readdir(dir)) != NULL) { 105 if (strcmp(entry->d_name, ".") == 0) 106 continue; 107 if (strcmp(entry->d_name, "..") == 0) 108 continue; 109 snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name); 110 snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name); 111 if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) { 112 fprintf(stderr, "Bind %s -> %s failed: %s\n", 113 netns_name, etc_name, strerror(errno)); 114 } 115 } 116 closedir(dir); 117 } 118 119 static int netns_exec(int argc, char **argv) 120 { 121 /* Setup the proper environment for apps that are not netns 122 * aware, and execute a program in that environment. 123 */ 124 const char *name, *cmd; 125 char net_path[MAXPATHLEN]; 126 int netns; 127 128 if (argc < 1) { 129 fprintf(stderr, "No netns name specified\n"); 130 return -1; 131 } 132 if (argc < 2) { 133 fprintf(stderr, "No cmd specified\n"); 134 return -1; 135 } 136 name = argv[0]; 137 cmd = argv[1]; 138 snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name); 139 netns = open(net_path, O_RDONLY); 140 if (netns < 0) { 141 fprintf(stderr, "Cannot open network namespace: %s\n", 142 strerror(errno)); 143 return -1; 144 } 145 if (setns(netns, CLONE_NEWNET) < 0) { 146 fprintf(stderr, "seting the network namespace failed: %s\n", 147 strerror(errno)); 148 return -1; 149 } 150 151 if (unshare(CLONE_NEWNS) < 0) { 152 fprintf(stderr, "unshare failed: %s\n", strerror(errno)); 153 return -1; 154 } 155 /* Mount a version of /sys that describes the network namespace */ 156 if (umount2("/sys", MNT_DETACH) < 0) { 157 fprintf(stderr, "umount of /sys failed: %s\n", strerror(errno)); 158 return -1; 159 } 160 if (mount(name, "/sys", "sysfs", 0, NULL) < 0) { 161 fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno)); 162 return -1; 163 } 164 165 /* Setup bind mounts for config files in /etc */ 166 bind_etc(name); 167 168 if (execvp(cmd, argv + 1) < 0) 169 fprintf(stderr, "exec of %s failed: %s\n", 170 cmd, strerror(errno)); 171 exit(-1); 172 } 173 174 static int netns_delete(int argc, char **argv) 175 { 176 const char *name; 177 char netns_path[MAXPATHLEN]; 178 179 if (argc < 1) { 180 fprintf(stderr, "No netns name specified\n"); 181 return -1; 182 } 183 184 name = argv[0]; 185 snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name); 186 umount2(netns_path, MNT_DETACH); 187 if (unlink(netns_path) < 0) { 188 fprintf(stderr, "Cannot remove %s: %s\n", 189 netns_path, strerror(errno)); 190 return -1; 191 } 192 return 0; 193 } 194 195 static int netns_add(int argc, char **argv) 196 { 197 /* This function creates a new network namespace and 198 * a new mount namespace and bind them into a well known 199 * location in the filesystem based on the name provided. 200 * 201 * The mount namespace is created so that any necessary 202 * userspace tweaks like remounting /sys, or bind mounting 203 * a new /etc/resolv.conf can be shared between uers. 204 */ 205 char netns_path[MAXPATHLEN]; 206 const char *name; 207 int fd; 208 209 if (argc < 1) { 210 fprintf(stderr, "No netns name specified\n"); 211 return -1; 212 } 213 name = argv[0]; 214 215 snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name); 216 217 /* Create the base netns directory if it doesn't exist */ 218 mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); 219 220 /* Create the filesystem state */ 221 fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0); 222 if (fd < 0) { 223 fprintf(stderr, "Could not create %s: %s\n", 224 netns_path, strerror(errno)); 225 return -1; 226 } 227 close(fd); 228 if (unshare(CLONE_NEWNET) < 0) { 229 fprintf(stderr, "Failed to create a new network namespace: %s\n", 230 strerror(errno)); 231 goto out_delete; 232 } 233 234 /* Bind the netns last so I can watch for it */ 235 if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0) { 236 fprintf(stderr, "Bind /proc/self/ns/net -> %s failed: %s\n", 237 netns_path, strerror(errno)); 238 goto out_delete; 239 } 240 return 0; 241 out_delete: 242 netns_delete(argc, argv); 243 exit(-1); 244 return -1; 245 } 246 247 248 static int netns_monitor(int argc, char **argv) 249 { 250 char buf[4096]; 251 struct inotify_event *event; 252 int fd; 253 fd = inotify_init(); 254 if (fd < 0) { 255 fprintf(stderr, "inotify_init failed: %s\n", 256 strerror(errno)); 257 return -1; 258 } 259 if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) { 260 fprintf(stderr, "inotify_add_watch failed: %s\n", 261 strerror(errno)); 262 return -1; 263 } 264 for(;;) { 265 ssize_t len = read(fd, buf, sizeof(buf)); 266 if (len < 0) { 267 fprintf(stderr, "read failed: %s\n", 268 strerror(errno)); 269 return -1; 270 } 271 for (event = (struct inotify_event *)buf; 272 (char *)event < &buf[len]; 273 event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len)) { 274 if (event->mask & IN_CREATE) 275 printf("add %s\n", event->name); 276 if (event->mask & IN_DELETE) 277 printf("delete %s\n", event->name); 278 } 279 } 280 return 0; 281 } 282 283 int do_netns(int argc, char **argv) 284 { 285 if (argc < 1) 286 return netns_list(0, NULL); 287 288 if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) || 289 (matches(*argv, "lst") == 0)) 290 return netns_list(argc-1, argv+1); 291 292 if (matches(*argv, "help") == 0) 293 usage(); 294 295 if (matches(*argv, "add") == 0) 296 return netns_add(argc-1, argv+1); 297 298 if (matches(*argv, "delete") == 0) 299 return netns_delete(argc-1, argv+1); 300 301 if (matches(*argv, "exec") == 0) 302 return netns_exec(argc-1, argv+1); 303 304 if (matches(*argv, "monitor") == 0) 305 return netns_monitor(argc-1, argv+1); 306 307 fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv); 308 exit(-1); 309 } 310