diff --git a/Makefile b/Makefile index 8477cf0..28ef316 100644 --- a/Makefile +++ b/Makefile @@ -153,6 +153,7 @@ pkgs: # - android-cloexec-pipe # - android-cloexec-pipe2 # - android-cloexec-epoll-create1 +# - android-cloexec-inotify-init1 # TODO: check, fix except for the few cases where we need to share fds # # - bugprone-narrowing-conversions @@ -197,7 +198,7 @@ clang-tidy: $(wildcard *.c) $(wildcard *.h) -cppcoreguidelines-avoid-magic-numbers,\ -readability-isolate-declaration,\ -android-cloexec-open,-android-cloexec-pipe,-android-cloexec-pipe2,\ - -android-cloexec-epoll-create1,\ + -android-cloexec-epoll-create1,-android-cloexec-inotify-init1,\ -bugprone-narrowing-conversions,\ -cppcoreguidelines-narrowing-conversions,\ -cppcoreguidelines-avoid-non-const-global-variables,\ diff --git a/conf.c b/conf.c index 21e9bc0..9851575 100644 --- a/conf.c +++ b/conf.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -414,20 +415,34 @@ static int conf_ns_opt(struct ctx *c, nfd = open(netns, O_RDONLY); - if (nfd >= 0 && (ufd >= 0 || c->netns_only)) { - c->pasta_netns_fd = nfd; - c->pasta_userns_fd = ufd; + if (nfd == -1 || (ufd == -1 && !c->netns_only)) { + if (nfd >= 0) + close(nfd); - NS_CALL(conf_ns_check, c); - if (c->pasta_netns_fd >= 0) - return 0; + if (ufd >= 0) + close(ufd); + + continue; } - if (nfd >= 0) - close(nfd); + c->pasta_netns_fd = nfd; + c->pasta_userns_fd = ufd; - if (ufd >= 0) - close(ufd); + NS_CALL(conf_ns_check, c); + + if (c->pasta_netns_fd >= 0) { + char buf[PATH_MAX]; + + if (try == 0 || c->no_netns_quit) + return 0; + + strncpy(buf, netns, PATH_MAX); + strncpy(c->netns_base, basename(buf), PATH_MAX - 1); + strncpy(buf, netns, PATH_MAX); + strncpy(c->netns_dir, dirname(buf), PATH_MAX - 1); + + return 0; + } } c->netns_only = netns_only_reset; @@ -813,6 +828,7 @@ void conf(struct ctx *c, int argc, char **argv) {"dhcp-search", no_argument, NULL, 7 }, {"no-dhcp-search", no_argument, NULL, 8 }, {"dns-forward", required_argument, NULL, 9 }, + {"no-netns-quit", no_argument, NULL, 10 }, { 0 }, }; struct get_bound_ports_ns_arg ns_ports_arg = { .c = c }; @@ -937,6 +953,13 @@ void conf(struct ctx *c, int argc, char **argv) err("Invalid DNS forwarding address: %s", optarg); usage(argv[0]); break; + case 10: + if (c->mode != MODE_PASTA) { + err("--no-netns-quit is for pasta mode only"); + usage(argv[0]); + } + c->no_netns_quit = 1; + break; case 'd': if (c->debug) { err("Multiple --debug options given"); diff --git a/passt.1 b/passt.1 index 7070a31..485e1db 100644 --- a/passt.1 +++ b/passt.1 @@ -426,6 +426,11 @@ Join only a target network namespace, not a user namespace, and don't create one for sandboxing purposes either. This is implied if PATH or NAME are given without \-\-userns. +.TP +.BR \-\-no-netns-quit +If the target network namespace is bound to the filesystem (that is, if PATH or +NAME are given as target), do not exit once the network namespace is deleted. + .TP .BR \-\-nsrun-dir " " \fIpath Directory for nsfs mountpoints, used as path prefix for names of namespaces. diff --git a/passt.c b/passt.c index 67ad1c7..36f0161 100644 --- a/passt.c +++ b/passt.c @@ -301,7 +301,7 @@ void exit_handler(int signal) */ int main(int argc, char **argv) { - int nfds, i, devnull_fd = -1, pidfile_fd = -1; + int nfds, i, devnull_fd = -1, pidfile_fd = -1, quit_fd; struct epoll_event events[EPOLL_EVENTS]; struct ctx c = { 0 }; struct rlimit limit; @@ -357,6 +357,8 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + quit_fd = pasta_netns_quit_init(&c); + if (getrlimit(RLIMIT_NOFILE, &limit)) { perror("getrlimit"); exit(EXIT_FAILURE); @@ -416,6 +418,7 @@ int main(int argc, char **argv) seccomp(&c); timer_init(&c, &now); + loop: nfds = epoll_wait(c.epollfd, events, EPOLL_EVENTS, TIMER_INTERVAL); if (nfds == -1 && errno != EINTR) { @@ -431,6 +434,8 @@ loop: if (fd == c.fd_tap || fd == c.fd_tap_listen) tap_handler(&c, fd, events[i].events, &now); + else if (fd == quit_fd) + pasta_netns_quit_handler(&c, fd); else sock_handler(&c, ref, events[i].events, &now); } diff --git a/passt.h b/passt.h index 2589ee7..042f760 100644 --- a/passt.h +++ b/passt.h @@ -101,6 +101,9 @@ enum passt_modes { * @pasta_netns_fd: File descriptor for network namespace in pasta mode * @pasta_userns_fd: Descriptor for user namespace to join, -1 once joined * @netns_only: In pasta mode, don't join or create a user namespace + * @no_netns_quit: In pasta mode, don't exit if fs-bound namespace is gone + * @netns_base: Base name for fs-bound namespace, if any, in pasta mode + * @netns_dir: Directory of fs-bound namespace, if any, in pasta mode * @proc_net_tcp: Stored handles for /proc/net/tcp{,6} in init and ns * @proc_net_udp: Stored handles for /proc/net/udp{,6} in init and ns * @epollfd: File descriptor for epoll instance @@ -161,6 +164,10 @@ struct ctx { int pasta_userns_fd; int netns_only; + int no_netns_quit; + char netns_base[PATH_MAX]; + char netns_dir[PATH_MAX]; + int proc_net_tcp[IP_VERSIONS][2]; int proc_net_udp[IP_VERSIONS][2]; diff --git a/pasta.c b/pasta.c index 972cbcf..e45cc92 100644 --- a/pasta.c +++ b/pasta.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include #include #include @@ -219,3 +221,53 @@ void pasta_ns_conf(struct ctx *c) proto_update_l2_buf(c->mac_guest, NULL, NULL); } + +/** + * pasta_netns_quit_init() - Watch network namespace to quit once it's gone + * @c: Execution context + * + * Return: inotify file descriptor, -1 on failure or if not needed/applicable + */ +int pasta_netns_quit_init(struct ctx *c) +{ + struct epoll_event ev = { .events = EPOLLIN }; + int inotify_fd; + + if (c->mode != MODE_PASTA || c->no_netns_quit || !*c->netns_base) + return -1; + + if ((inotify_fd = inotify_init1(O_NONBLOCK)) < 0) { + perror("inotify_init(): won't quit once netns is gone"); + return -1; + } + + if (inotify_add_watch(inotify_fd, c->netns_dir, IN_DELETE) < 0) { + perror("inotify_add_watch(): won't quit once netns is gone"); + return -1; + } + + ev.data.fd = inotify_fd; + epoll_ctl(c->epollfd, EPOLL_CTL_ADD, inotify_fd, &ev); + + return inotify_fd; +} + +/** + * pasta_netns_quit_handler() - Handle ns directory events, exit if ns is gone + * @c: Execution context + * @inotify_fd: inotify file descriptor with watch on namespace directory + */ +void pasta_netns_quit_handler(struct ctx *c, int inotify_fd) +{ + char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; + struct inotify_event *in_ev = (struct inotify_event *)buf; + + if (read(inotify_fd, buf, sizeof(buf)) < (ssize_t)sizeof(*in_ev)) + return; + + if (strncmp(in_ev->name, c->netns_base, sizeof(c->netns_base))) + return; + + info("Namespace %s is gone, exiting", c->netns_base); + exit(EXIT_SUCCESS); +} diff --git a/pasta.h b/pasta.h index 1fcd6a9..235bfb9 100644 --- a/pasta.h +++ b/pasta.h @@ -6,3 +6,5 @@ void pasta_start_ns(struct ctx *c); void pasta_ns_conf(struct ctx *c); void pasta_child_handler(int signal); +int pasta_netns_quit_init(struct ctx *c); +void pasta_netns_quit_handler(struct ctx *c, int inotify_fd);