When a user spawns a command with pasta they expect the network to be ready. Currently this does not work because pasta will fork/exec before it will setup the network config. This patch fixes it by using a pipe to sync parent and child. The child will now block reading from this pipe before the exec call. The parent will then unblock the child only after the netns was configured. A command like `pasta --config-net -- ping -c1 1.1.1.1` can now actually work as expected. Signed-off-by: Paul Holzinger <pholzing(a)redhat.com> --- passt.c | 9 ++++++++- passt.h | 3 +++ pasta.c | 31 ++++++++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/passt.c b/passt.c index 8b2c50d..4ef5797 100644 --- a/passt.c +++ b/passt.c @@ -187,7 +187,8 @@ int main(int argc, char **argv) isolate_initial(); - c.pasta_netns_fd = c.fd_tap = c.fd_tap_listen = -1; + c.pasta_netns_fd = c.pasta_command_ready_fd = + c.fd_tap = c.fd_tap_listen = -1; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; @@ -296,6 +297,12 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + /* start pasta child process now after the netns is setup */ + if (c.pasta_command_ready_fd > -1) { + /* close causes EOF for the read in the child so no need to write() */ + close(c.pasta_command_ready_fd); + } + if (!c.foreground) __daemon(pidfile_fd, devnull_fd); else diff --git a/passt.h b/passt.h index 3d7e567..a78cd81 100644 --- a/passt.h +++ b/passt.h @@ -154,6 +154,8 @@ struct ip6_ctx { * @pcap: Path for packet capture file * @pid_file: Path to PID file, empty string if not configured * @pasta_netns_fd: File descriptor for network namespace in pasta mode + * @pasta_command_ready_fd: File descriptor for the ready pipe to + * start child cmd, -1 if not used * @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 @@ -205,6 +207,7 @@ struct ctx { int one_off; int pasta_netns_fd; + int pasta_command_ready_fd; int no_netns_quit; char netns_base[PATH_MAX]; diff --git a/pasta.c b/pasta.c index 528f02a..56ac326 100644 --- a/pasta.c +++ b/pasta.c @@ -149,16 +149,19 @@ void pasta_open_ns(struct ctx *c, const char *netns) /** * struct pasta_spawn_cmd_arg - Argument for pasta_spawn_cmd() - * @exe: Executable to run - * @argv: Command and arguments to run + * @exe: Executable to run + * @argv: Command and arguments to run + * @ready_pipe: Ready pipe pair from parent. */ struct pasta_spawn_cmd_arg { const char *exe; char *const *argv; + int ready_pipe[2]; }; /** - * pasta_spawn_cmd() - Prepare new netns, start command or shell + * pasta_spawn_cmd() - Prepare new netns, spawn child, wait for parent, + * then exec command or shell * @arg: See @pasta_spawn_cmd_arg * * Return: this function never returns @@ -166,11 +169,24 @@ struct pasta_spawn_cmd_arg { static int pasta_spawn_cmd(void *arg) { const struct pasta_spawn_cmd_arg *a; + char buf[1]; if (write_file("/proc/sys/net/ipv4/ping_group_range", "0 0")) warn("Cannot set ping_group_range, ICMP requests might fail"); a = (const struct pasta_spawn_cmd_arg *)arg; + + /* close write side, we want read to return EOF when parent closes the fd */ + close(a->ready_pipe[1]); + + /* wait here for parent setup to finish before we exec */ + if (TEMP_FAILURE_RETRY(read(a->ready_pipe[0], buf, sizeof(buf))) < 0) { + err("Failed to read ready pipe from parent: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } + close(a->ready_pipe[0]); + execvp(a->exe, a->argv); perror("execvp"); @@ -226,6 +242,13 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid, arg.argv = sh_argv; } + if (pipe(arg.ready_pipe) < 0) { + err("Create ready pipe: %s", strerror(errno)); + exit(EXIT_FAILURE); + } + + c->pasta_command_ready_fd = arg.ready_pipe[1]; + pasta_child_pid = do_clone(pasta_spawn_cmd, ns_fn_stack, sizeof(ns_fn_stack), CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET | @@ -237,6 +260,8 @@ void pasta_start_ns(struct ctx *c, uid_t uid, gid_t gid, exit(EXIT_FAILURE); } + close(arg.ready_pipe[0]); + NS_CALL(pasta_wait_for_ns, c); } -- 2.39.1