]> git.proxmox.com Git - mirror_iproute2.git/blob - ip/ipnetns.c
libnetlink: introduce rtnl_listen_filter_t
[mirror_iproute2.git] / ip / ipnetns.c
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 #include <linux/limits.h>
18
19 #include <linux/net_namespace.h>
20
21 #include "utils.h"
22 #include "hlist.h"
23 #include "ip_common.h"
24 #include "namespace.h"
25
26 static int usage(void)
27 {
28 fprintf(stderr, "Usage: ip netns list\n");
29 fprintf(stderr, " ip netns add NAME\n");
30 fprintf(stderr, " ip netns set NAME NETNSID\n");
31 fprintf(stderr, " ip [-all] netns delete [NAME]\n");
32 fprintf(stderr, " ip netns identify [PID]\n");
33 fprintf(stderr, " ip netns pids NAME\n");
34 fprintf(stderr, " ip [-all] netns exec [NAME] cmd ...\n");
35 fprintf(stderr, " ip netns monitor\n");
36 fprintf(stderr, " ip netns list-id\n");
37 exit(-1);
38 }
39
40 /* This socket is used to get nsid */
41 static struct rtnl_handle rtnsh = { .fd = -1 };
42
43 static int have_rtnl_getnsid = -1;
44
45 static int ipnetns_accept_msg(const struct sockaddr_nl *who,
46 struct rtnl_ctrl_data *ctrl,
47 struct nlmsghdr *n, void *arg)
48 {
49 struct nlmsgerr *err = (struct nlmsgerr *)NLMSG_DATA(n);
50
51 if (n->nlmsg_type == NLMSG_ERROR &&
52 (err->error == -EOPNOTSUPP || err->error == -EINVAL))
53 have_rtnl_getnsid = 0;
54 else
55 have_rtnl_getnsid = 1;
56 return -1;
57 }
58
59 static int ipnetns_have_nsid(void)
60 {
61 struct {
62 struct nlmsghdr n;
63 struct rtgenmsg g;
64 char buf[1024];
65 } req;
66 int fd;
67
68 if (have_rtnl_getnsid < 0) {
69 memset(&req, 0, sizeof(req));
70 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
71 req.n.nlmsg_flags = NLM_F_REQUEST;
72 req.n.nlmsg_type = RTM_GETNSID;
73 req.g.rtgen_family = AF_UNSPEC;
74
75 fd = open("/proc/self/ns/net", O_RDONLY);
76 if (fd < 0) {
77 perror("open(\"/proc/self/ns/net\")");
78 exit(1);
79 }
80
81 addattr32(&req.n, 1024, NETNSA_FD, fd);
82
83 if (rtnl_send(&rth, &req.n, req.n.nlmsg_len) < 0) {
84 perror("request send failed");
85 exit(1);
86 }
87 rtnl_listen(&rth, ipnetns_accept_msg, NULL);
88 close(fd);
89 }
90
91 return have_rtnl_getnsid;
92 }
93
94 static int get_netnsid_from_name(const char *name)
95 {
96 struct {
97 struct nlmsghdr n;
98 struct rtgenmsg g;
99 char buf[1024];
100 } req, answer;
101 struct rtattr *tb[NETNSA_MAX + 1];
102 struct rtgenmsg *rthdr;
103 int len, fd;
104
105 memset(&req, 0, sizeof(req));
106 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
107 req.n.nlmsg_flags = NLM_F_REQUEST;
108 req.n.nlmsg_type = RTM_GETNSID;
109 req.g.rtgen_family = AF_UNSPEC;
110
111 fd = netns_get_fd(name);
112 if (fd < 0)
113 return fd;
114
115 addattr32(&req.n, 1024, NETNSA_FD, fd);
116 if (rtnl_talk(&rtnsh, &req.n, 0, 0, &answer.n) < 0) {
117 close(fd);
118 return -2;
119 }
120 close(fd);
121
122 /* Validate message and parse attributes */
123 if (answer.n.nlmsg_type == NLMSG_ERROR)
124 return -1;
125
126 rthdr = NLMSG_DATA(&answer.n);
127 len = answer.n.nlmsg_len - NLMSG_SPACE(sizeof(*rthdr));
128 if (len < 0)
129 return -1;
130
131 parse_rtattr(tb, NETNSA_MAX, NETNS_RTA(rthdr), len);
132
133 if (tb[NETNSA_NSID])
134 return rta_getattr_u32(tb[NETNSA_NSID]);
135
136 return -1;
137 }
138
139 struct nsid_cache {
140 struct hlist_node nsid_hash;
141 struct hlist_node name_hash;
142 int nsid;
143 char name[NAME_MAX];
144 };
145
146 #define NSIDMAP_SIZE 128
147 #define NSID_HASH_NSID(nsid) (nsid & (NSIDMAP_SIZE - 1))
148 #define NSID_HASH_NAME(name) (namehash(name) & (NSIDMAP_SIZE - 1))
149
150 static struct hlist_head nsid_head[NSIDMAP_SIZE];
151 static struct hlist_head name_head[NSIDMAP_SIZE];
152
153 static struct nsid_cache *netns_map_get_by_nsid(int nsid)
154 {
155 uint32_t h = NSID_HASH_NSID(nsid);
156 struct hlist_node *n;
157
158 hlist_for_each(n, &nsid_head[h]) {
159 struct nsid_cache *c = container_of(n, struct nsid_cache,
160 nsid_hash);
161 if (c->nsid == nsid)
162 return c;
163 }
164
165 return NULL;
166 }
167
168 static int netns_map_add(int nsid, char *name)
169 {
170 struct nsid_cache *c;
171 uint32_t h;
172
173 if (netns_map_get_by_nsid(nsid) != NULL)
174 return -EEXIST;
175
176 c = malloc(sizeof(*c));
177 if (c == NULL) {
178 perror("malloc");
179 return -ENOMEM;
180 }
181 c->nsid = nsid;
182 strcpy(c->name, name);
183
184 h = NSID_HASH_NSID(nsid);
185 hlist_add_head(&c->nsid_hash, &nsid_head[h]);
186
187 h = NSID_HASH_NAME(name);
188 hlist_add_head(&c->name_hash, &name_head[h]);
189
190 return 0;
191 }
192
193 static void netns_map_del(struct nsid_cache *c)
194 {
195 hlist_del(&c->name_hash);
196 hlist_del(&c->nsid_hash);
197 free(c);
198 }
199
200 void netns_map_init(void)
201 {
202 static int initialized;
203 struct dirent *entry;
204 DIR *dir;
205 int nsid;
206
207 if (initialized || !ipnetns_have_nsid())
208 return;
209
210 if (rtnl_open(&rtnsh, 0) < 0) {
211 fprintf(stderr, "Cannot open rtnetlink\n");
212 exit(1);
213 }
214
215 dir = opendir(NETNS_RUN_DIR);
216 if (!dir)
217 return;
218
219 while ((entry = readdir(dir)) != NULL) {
220 if (strcmp(entry->d_name, ".") == 0)
221 continue;
222 if (strcmp(entry->d_name, "..") == 0)
223 continue;
224 nsid = get_netnsid_from_name(entry->d_name);
225
226 if (nsid >= 0)
227 netns_map_add(nsid, entry->d_name);
228 }
229 closedir(dir);
230 initialized = 1;
231 }
232
233 static int netns_get_name(int nsid, char *name)
234 {
235 struct dirent *entry;
236 DIR *dir;
237 int id;
238
239 dir = opendir(NETNS_RUN_DIR);
240 if (!dir)
241 return -ENOENT;
242
243 while ((entry = readdir(dir)) != NULL) {
244 if (strcmp(entry->d_name, ".") == 0)
245 continue;
246 if (strcmp(entry->d_name, "..") == 0)
247 continue;
248 id = get_netnsid_from_name(entry->d_name);
249
250 if (nsid == id) {
251 strcpy(name, entry->d_name);
252 closedir(dir);
253 return 0;
254 }
255 }
256 closedir(dir);
257 return -ENOENT;
258 }
259
260 int print_nsid(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
261 {
262 struct rtgenmsg *rthdr = NLMSG_DATA(n);
263 struct rtattr *tb[NETNSA_MAX+1];
264 int len = n->nlmsg_len;
265 FILE *fp = (FILE *)arg;
266 struct nsid_cache *c;
267 char name[NAME_MAX];
268 int nsid;
269
270 if (n->nlmsg_type != RTM_NEWNSID && n->nlmsg_type != RTM_DELNSID)
271 return 0;
272
273 len -= NLMSG_SPACE(sizeof(*rthdr));
274 if (len < 0) {
275 fprintf(stderr, "BUG: wrong nlmsg len %d in %s\n", len,
276 __func__);
277 return -1;
278 }
279
280 parse_rtattr(tb, NETNSA_MAX, NETNS_RTA(rthdr), len);
281 if (tb[NETNSA_NSID] == NULL) {
282 fprintf(stderr, "BUG: NETNSA_NSID is missing %s\n", __func__);
283 return -1;
284 }
285
286 if (n->nlmsg_type == RTM_DELNSID)
287 fprintf(fp, "Deleted ");
288
289 nsid = rta_getattr_u32(tb[NETNSA_NSID]);
290 fprintf(fp, "nsid %u ", nsid);
291
292 c = netns_map_get_by_nsid(nsid);
293 if (c != NULL) {
294 fprintf(fp, "(iproute2 netns name: %s)", c->name);
295 netns_map_del(c);
296 }
297
298 /* During 'ip monitor nsid', no chance to have new nsid in cache. */
299 if (c == NULL && n->nlmsg_type == RTM_NEWNSID)
300 if (netns_get_name(nsid, name) == 0) {
301 fprintf(fp, "(iproute2 netns name: %s)", name);
302 netns_map_add(nsid, name);
303 }
304
305 fprintf(fp, "\n");
306 fflush(fp);
307 return 0;
308 }
309
310 static int netns_list_id(int argc, char **argv)
311 {
312 if (!ipnetns_have_nsid()) {
313 fprintf(stderr,
314 "RTM_GETNSID is not supported by the kernel.\n");
315 return -ENOTSUP;
316 }
317
318 if (rtnl_wilddump_request(&rth, AF_UNSPEC, RTM_GETNSID) < 0) {
319 perror("Cannot send dump request");
320 exit(1);
321 }
322 if (rtnl_dump_filter(&rth, print_nsid, stdout) < 0) {
323 fprintf(stderr, "Dump terminated\n");
324 exit(1);
325 }
326 return 0;
327 }
328
329 static int netns_list(int argc, char **argv)
330 {
331 struct dirent *entry;
332 DIR *dir;
333 int id;
334
335 dir = opendir(NETNS_RUN_DIR);
336 if (!dir)
337 return 0;
338
339 while ((entry = readdir(dir)) != NULL) {
340 if (strcmp(entry->d_name, ".") == 0)
341 continue;
342 if (strcmp(entry->d_name, "..") == 0)
343 continue;
344 printf("%s", entry->d_name);
345 if (ipnetns_have_nsid()) {
346 id = get_netnsid_from_name(entry->d_name);
347 if (id >= 0)
348 printf(" (id: %d)", id);
349 }
350 printf("\n");
351 }
352 closedir(dir);
353 return 0;
354 }
355
356 static int cmd_exec(const char *cmd, char **argv, bool do_fork)
357 {
358 fflush(stdout);
359 if (do_fork) {
360 int status;
361 pid_t pid;
362
363 pid = fork();
364 if (pid < 0) {
365 perror("fork");
366 exit(1);
367 }
368
369 if (pid != 0) {
370 /* Parent */
371 if (waitpid(pid, &status, 0) < 0) {
372 perror("waitpid");
373 exit(1);
374 }
375
376 if (WIFEXITED(status)) {
377 return WEXITSTATUS(status);
378 }
379
380 exit(1);
381 }
382 }
383
384 if (execvp(cmd, argv) < 0)
385 fprintf(stderr, "exec of \"%s\" failed: %s\n",
386 cmd, strerror(errno));
387 _exit(1);
388 }
389
390 static int on_netns_exec(char *nsname, void *arg)
391 {
392 char **argv = arg;
393 cmd_exec(argv[1], argv + 1, true);
394 return 0;
395 }
396
397 static int netns_exec(int argc, char **argv)
398 {
399 /* Setup the proper environment for apps that are not netns
400 * aware, and execute a program in that environment.
401 */
402 const char *cmd;
403
404 if (argc < 1 && !do_all) {
405 fprintf(stderr, "No netns name specified\n");
406 return -1;
407 }
408 if ((argc < 2 && !do_all) || (argc < 1 && do_all)) {
409 fprintf(stderr, "No command specified\n");
410 return -1;
411 }
412
413 if (do_all)
414 return do_each_netns(on_netns_exec, --argv, 1);
415
416 if (netns_switch(argv[0]))
417 return -1;
418
419 /* ip must return the status of the child,
420 * but do_cmd() will add a minus to this,
421 * so let's add another one here to cancel it.
422 */
423 cmd = argv[1];
424 return -cmd_exec(cmd, argv + 1, !!batch_mode);
425 }
426
427 static int is_pid(const char *str)
428 {
429 int ch;
430 for (; (ch = *str); str++) {
431 if (!isdigit(ch))
432 return 0;
433 }
434 return 1;
435 }
436
437 static int netns_pids(int argc, char **argv)
438 {
439 const char *name;
440 char net_path[MAXPATHLEN];
441 int netns;
442 struct stat netst;
443 DIR *dir;
444 struct dirent *entry;
445
446 if (argc < 1) {
447 fprintf(stderr, "No netns name specified\n");
448 return -1;
449 }
450 if (argc > 1) {
451 fprintf(stderr, "extra arguments specified\n");
452 return -1;
453 }
454
455 name = argv[0];
456 snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
457 netns = open(net_path, O_RDONLY);
458 if (netns < 0) {
459 fprintf(stderr, "Cannot open network namespace: %s\n",
460 strerror(errno));
461 return -1;
462 }
463 if (fstat(netns, &netst) < 0) {
464 fprintf(stderr, "Stat of netns failed: %s\n",
465 strerror(errno));
466 return -1;
467 }
468 dir = opendir("/proc/");
469 if (!dir) {
470 fprintf(stderr, "Open of /proc failed: %s\n",
471 strerror(errno));
472 return -1;
473 }
474 while((entry = readdir(dir))) {
475 char pid_net_path[MAXPATHLEN];
476 struct stat st;
477 if (!is_pid(entry->d_name))
478 continue;
479 snprintf(pid_net_path, sizeof(pid_net_path), "/proc/%s/ns/net",
480 entry->d_name);
481 if (stat(pid_net_path, &st) != 0)
482 continue;
483 if ((st.st_dev == netst.st_dev) &&
484 (st.st_ino == netst.st_ino)) {
485 printf("%s\n", entry->d_name);
486 }
487 }
488 closedir(dir);
489 return 0;
490
491 }
492
493 static int netns_identify(int argc, char **argv)
494 {
495 const char *pidstr;
496 char net_path[MAXPATHLEN];
497 int netns;
498 struct stat netst;
499 DIR *dir;
500 struct dirent *entry;
501
502 if (argc < 1) {
503 pidstr = "self";
504 } else if (argc > 1) {
505 fprintf(stderr, "extra arguments specified\n");
506 return -1;
507 } else {
508 pidstr = argv[0];
509 if (!is_pid(pidstr)) {
510 fprintf(stderr, "Specified string '%s' is not a pid\n",
511 pidstr);
512 return -1;
513 }
514 }
515
516 snprintf(net_path, sizeof(net_path), "/proc/%s/ns/net", pidstr);
517 netns = open(net_path, O_RDONLY);
518 if (netns < 0) {
519 fprintf(stderr, "Cannot open network namespace: %s\n",
520 strerror(errno));
521 return -1;
522 }
523 if (fstat(netns, &netst) < 0) {
524 fprintf(stderr, "Stat of netns failed: %s\n",
525 strerror(errno));
526 return -1;
527 }
528 dir = opendir(NETNS_RUN_DIR);
529 if (!dir) {
530 /* Succeed treat a missing directory as an empty directory */
531 if (errno == ENOENT)
532 return 0;
533
534 fprintf(stderr, "Failed to open directory %s:%s\n",
535 NETNS_RUN_DIR, strerror(errno));
536 return -1;
537 }
538
539 while((entry = readdir(dir))) {
540 char name_path[MAXPATHLEN];
541 struct stat st;
542
543 if (strcmp(entry->d_name, ".") == 0)
544 continue;
545 if (strcmp(entry->d_name, "..") == 0)
546 continue;
547
548 snprintf(name_path, sizeof(name_path), "%s/%s", NETNS_RUN_DIR,
549 entry->d_name);
550
551 if (stat(name_path, &st) != 0)
552 continue;
553
554 if ((st.st_dev == netst.st_dev) &&
555 (st.st_ino == netst.st_ino)) {
556 printf("%s\n", entry->d_name);
557 }
558 }
559 closedir(dir);
560 return 0;
561
562 }
563
564 static int on_netns_del(char *nsname, void *arg)
565 {
566 char netns_path[MAXPATHLEN];
567
568 snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, nsname);
569 umount2(netns_path, MNT_DETACH);
570 if (unlink(netns_path) < 0) {
571 fprintf(stderr, "Cannot remove namespace file \"%s\": %s\n",
572 netns_path, strerror(errno));
573 return -1;
574 }
575 return 0;
576 }
577
578 static int netns_delete(int argc, char **argv)
579 {
580 if (argc < 1 && !do_all) {
581 fprintf(stderr, "No netns name specified\n");
582 return -1;
583 }
584
585 if (do_all)
586 return netns_foreach(on_netns_del, NULL);
587
588 return on_netns_del(argv[0], NULL);
589 }
590
591 static int create_netns_dir(void)
592 {
593 /* Create the base netns directory if it doesn't exist */
594 if (mkdir(NETNS_RUN_DIR, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
595 if (errno != EEXIST) {
596 fprintf(stderr, "mkdir %s failed: %s\n",
597 NETNS_RUN_DIR, strerror(errno));
598 return -1;
599 }
600 }
601
602 return 0;
603 }
604
605 static int netns_add(int argc, char **argv)
606 {
607 /* This function creates a new network namespace and
608 * a new mount namespace and bind them into a well known
609 * location in the filesystem based on the name provided.
610 *
611 * The mount namespace is created so that any necessary
612 * userspace tweaks like remounting /sys, or bind mounting
613 * a new /etc/resolv.conf can be shared between uers.
614 */
615 char netns_path[MAXPATHLEN];
616 const char *name;
617 int fd;
618 int made_netns_run_dir_mount = 0;
619
620 if (argc < 1) {
621 fprintf(stderr, "No netns name specified\n");
622 return -1;
623 }
624 name = argv[0];
625
626 snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
627
628 if (create_netns_dir())
629 return -1;
630
631 /* Make it possible for network namespace mounts to propagate between
632 * mount namespaces. This makes it likely that a unmounting a network
633 * namespace file in one namespace will unmount the network namespace
634 * file in all namespaces allowing the network namespace to be freed
635 * sooner.
636 */
637 while (mount("", NETNS_RUN_DIR, "none", MS_SHARED | MS_REC, NULL)) {
638 /* Fail unless we need to make the mount point */
639 if (errno != EINVAL || made_netns_run_dir_mount) {
640 fprintf(stderr, "mount --make-shared %s failed: %s\n",
641 NETNS_RUN_DIR, strerror(errno));
642 return -1;
643 }
644
645 /* Upgrade NETNS_RUN_DIR to a mount point */
646 if (mount(NETNS_RUN_DIR, NETNS_RUN_DIR, "none", MS_BIND, NULL)) {
647 fprintf(stderr, "mount --bind %s %s failed: %s\n",
648 NETNS_RUN_DIR, NETNS_RUN_DIR, strerror(errno));
649 return -1;
650 }
651 made_netns_run_dir_mount = 1;
652 }
653
654 /* Create the filesystem state */
655 fd = open(netns_path, O_RDONLY|O_CREAT|O_EXCL, 0);
656 if (fd < 0) {
657 fprintf(stderr, "Cannot create namespace file \"%s\": %s\n",
658 netns_path, strerror(errno));
659 return -1;
660 }
661 close(fd);
662 if (unshare(CLONE_NEWNET) < 0) {
663 fprintf(stderr, "Failed to create a new network namespace \"%s\": %s\n",
664 name, strerror(errno));
665 goto out_delete;
666 }
667
668 /* Bind the netns last so I can watch for it */
669 if (mount("/proc/self/ns/net", netns_path, "none", MS_BIND, NULL) < 0) {
670 fprintf(stderr, "Bind /proc/self/ns/net -> %s failed: %s\n",
671 netns_path, strerror(errno));
672 goto out_delete;
673 }
674 return 0;
675 out_delete:
676 netns_delete(argc, argv);
677 return -1;
678 }
679
680 static int set_netnsid_from_name(const char *name, int nsid)
681 {
682 struct {
683 struct nlmsghdr n;
684 struct rtgenmsg g;
685 char buf[1024];
686 } req;
687 int fd, err = 0;
688
689 memset(&req, 0, sizeof(req));
690 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
691 req.n.nlmsg_flags = NLM_F_REQUEST;
692 req.n.nlmsg_type = RTM_NEWNSID;
693 req.g.rtgen_family = AF_UNSPEC;
694
695 fd = netns_get_fd(name);
696 if (fd < 0)
697 return fd;
698
699 addattr32(&req.n, 1024, NETNSA_FD, fd);
700 addattr32(&req.n, 1024, NETNSA_NSID, nsid);
701 if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
702 err = -2;
703
704 close(fd);
705 return err;
706 }
707
708 static int netns_set(int argc, char **argv)
709 {
710 char netns_path[MAXPATHLEN];
711 const char *name;
712 int netns, nsid;
713
714 if (argc < 1) {
715 fprintf(stderr, "No netns name specified\n");
716 return -1;
717 }
718 if (argc < 2) {
719 fprintf(stderr, "No nsid specified\n");
720 return -1;
721 }
722 name = argv[0];
723 nsid = atoi(argv[1]);
724
725 snprintf(netns_path, sizeof(netns_path), "%s/%s", NETNS_RUN_DIR, name);
726 netns = open(netns_path, O_RDONLY | O_CLOEXEC);
727 if (netns < 0) {
728 fprintf(stderr, "Cannot open network namespace \"%s\": %s\n",
729 name, strerror(errno));
730 return -1;
731 }
732
733 return set_netnsid_from_name(name, nsid);
734 }
735
736 static int netns_monitor(int argc, char **argv)
737 {
738 char buf[4096];
739 struct inotify_event *event;
740 int fd;
741 fd = inotify_init();
742 if (fd < 0) {
743 fprintf(stderr, "inotify_init failed: %s\n",
744 strerror(errno));
745 return -1;
746 }
747
748 if (create_netns_dir())
749 return -1;
750
751 if (inotify_add_watch(fd, NETNS_RUN_DIR, IN_CREATE | IN_DELETE) < 0) {
752 fprintf(stderr, "inotify_add_watch failed: %s\n",
753 strerror(errno));
754 return -1;
755 }
756 for(;;) {
757 ssize_t len = read(fd, buf, sizeof(buf));
758 if (len < 0) {
759 fprintf(stderr, "read failed: %s\n",
760 strerror(errno));
761 return -1;
762 }
763 for (event = (struct inotify_event *)buf;
764 (char *)event < &buf[len];
765 event = (struct inotify_event *)((char *)event + sizeof(*event) + event->len)) {
766 if (event->mask & IN_CREATE)
767 printf("add %s\n", event->name);
768 if (event->mask & IN_DELETE)
769 printf("delete %s\n", event->name);
770 }
771 }
772 return 0;
773 }
774
775 int do_netns(int argc, char **argv)
776 {
777 netns_map_init();
778
779 if (argc < 1)
780 return netns_list(0, NULL);
781
782 if ((matches(*argv, "list") == 0) || (matches(*argv, "show") == 0) ||
783 (matches(*argv, "lst") == 0))
784 return netns_list(argc-1, argv+1);
785
786 if ((matches(*argv, "list-id") == 0))
787 return netns_list_id(argc-1, argv+1);
788
789 if (matches(*argv, "help") == 0)
790 return usage();
791
792 if (matches(*argv, "add") == 0)
793 return netns_add(argc-1, argv+1);
794
795 if (matches(*argv, "set") == 0)
796 return netns_set(argc-1, argv+1);
797
798 if (matches(*argv, "delete") == 0)
799 return netns_delete(argc-1, argv+1);
800
801 if (matches(*argv, "identify") == 0)
802 return netns_identify(argc-1, argv+1);
803
804 if (matches(*argv, "pids") == 0)
805 return netns_pids(argc-1, argv+1);
806
807 if (matches(*argv, "exec") == 0)
808 return netns_exec(argc-1, argv+1);
809
810 if (matches(*argv, "monitor") == 0)
811 return netns_monitor(argc-1, argv+1);
812
813 fprintf(stderr, "Command \"%s\" is unknown, try \"ip netns help\".\n", *argv);
814 exit(-1);
815 }