public inbox for passt-dev@passt.top
 help / color / mirror / code / Atom feed
* [PATCH] pasta: wait for netns setup before calling exec
@ 2023-02-01 18:01 Paul Holzinger
  2023-02-02 10:25 ` Stefano Brivio
  0 siblings, 1 reply; 11+ messages in thread
From: Paul Holzinger @ 2023-02-01 18:01 UTC (permalink / raw)
  To: passt-dev; +Cc: Paul Holzinger

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@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);
 }
 
-- 
@@ -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


^ permalink raw reply related	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2023-02-08 15:06 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-01 18:01 [PATCH] pasta: wait for netns setup before calling exec Paul Holzinger
2023-02-02 10:25 ` Stefano Brivio
2023-02-02 15:23   ` Paul Holzinger
2023-02-03 14:44   ` Paul Holzinger
2023-02-03 16:37     ` Stefano Brivio
2023-02-03 18:55       ` Stefano Brivio
2023-02-06 19:53         ` Paul Holzinger
2023-02-07 10:55           ` Stefano Brivio
2023-02-07 19:09             ` Paul Holzinger
2023-02-08 13:01               ` Stefano Brivio
2023-02-08 15:06                 ` Paul Holzinger

Code repositories for project(s) associated with this public inbox

	https://passt.top/passt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for IMAP folder(s).