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