On 7/13/23 19:10, Laszlo Ersek wrote: > On QEMU 7.2.0+, if "passt" is available, ask QEMU for passt ("stream") > rather than SLIRP ("user") networking. > > For this, we need to run passt ourselves. Given that passt daemonizes by > default, start it with our traditional function guestfs_int_cmd_run(). Ask > passt to save its PID file, because in case something goes wrong before > we're completely sure the appliance (i.e. QEMU) is up and running, we'll > need to kill passt, the *grandchild*, ourselves. > > Pass "--one-off" to passt (same as libvirt). This way, once we have proof > that QEMU has connected to passt (because the appliance shows signs of > life), we need not clean up passt ourselves -- once QEMU exits, passt will > see an EOF on the unix domain socket, and exit as well. > > Passt is way more flexible than SLIRP, and passt normally intends to > imitate the host environment in the guest as much as possible. This means > that, when switching from SLIRP to passt, the guest would see changes to > the following: > > - guest IP address, > > - guest subnet mask, > > - host (= gateway) IP address, > > - host (= gateway) MAC address. > > Extract the SLIRP defaults into the new macros NETWORK_GW_IP and > NETWORK_GW_MAC, and pass them explicitly to passt. In particular, > "tests/rsync/test-rsync.sh" fails without setting the host address > (NETWORK_GW_IP) properly. > > (These artifacts can be verified in the appliance with "virt-rescue > --network", by running "ip addr", "ip route", and "ip neighbor" at the > virt-rescue prompt. There are four scenarios: two libguest backends, times > passt being installed or not installed.) > > Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2184967 > Signed-off-by: Laszlo Ersek <ler...@redhat.com> > --- > lib/guestfs-internal.h | 26 ++++ > lib/launch-direct.c | 147 +++++++++++++++++++- > 2 files changed, 168 insertions(+), 5 deletions(-) > > diff --git a/lib/guestfs-internal.h b/lib/guestfs-internal.h > index 9ba4d4ad46cf..9f4f800e6d6e 100644 > --- a/lib/guestfs-internal.h > +++ b/lib/guestfs-internal.h > @@ -158,6 +158,32 @@ cleanup_mutex_unlock (pthread_mutex_t **ptr) > #define NETWORK_ADDRESS "169.254.2.15" > #define NETWORK_PREFIX "16" > > +/* The IP address and the MAC address of the host, as seen from the > appliance. > + * > + * NETWORK_GW_IP should be the same as NETWORK_ADDRESS, only replacing ".15" > in > + * the rightmost octet with ".2". NETWORK_GW_MAC cannot be changed. These > + * restrictions are a consequence of the following landscape: > + * > + * libguestfs backend userspace network stack restrictions > + * ------------------ ----------------------- > -------------------------------- > + * direct passt None; both NETWORK_GW_IP and > + * NETWORK_GW_MAC can be set on > the > + * passt command line. > + * > + * direct SLIRP SLIRP hard-codes > NETWORK_GW_MAC. > + * > + * libvirt passt The domain XML does not > expose > + * either knob (RHBZ#2222766), > even > + * though passt could accept > both. > + * > + * libvirt SLIRP The domain XML does not > expose > + * either knob (RHBZ#2222766), > and > + * SLIRP hard-codes > NETWORK_GW_MAC > + * anyway. > + */ > +#define NETWORK_GW_IP "169.254.2.2" > +#define NETWORK_GW_MAC "52:56:00:00:00:02" > + > /* Guestfs handle and associated structures. */ > > /* State. */ > diff --git a/lib/launch-direct.c b/lib/launch-direct.c > index 3f46f0509736..8d6ad025a4e1 100644 > --- a/lib/launch-direct.c > +++ b/lib/launch-direct.c > @@ -32,6 +32,7 @@ > #include <errno.h> > #include <fcntl.h> > #include <sys/types.h> > +#include <sys/wait.h> > #include <sys/time.h> > #include <sys/resource.h> > #include <sys/stat.h> > @@ -48,6 +49,7 @@ > #include "guestfs-internal.h" > #include "guestfs_protocol.h" > #include "qemuopts.h" > +#include "ignore-value.h" > > /* Per-handle data. */ > struct backend_direct_data { > @@ -333,6 +335,110 @@ add_drives (guestfs_h *g, struct backend_direct_data > *data, > return 0; > } > > +/** > + * Launch passt such that it daemonizes. > + * > + * On error, -1 is returned; C<passt_pid> and C<sockpath> are not modified. > + * > + * On success, 0 is returned. C<passt_pid> contains the PID of the passt > + * background process. C<sockpath> contains the pathname of the unix domain > + * socket where passt will accept a single connection. > + */
Arguably, I should have typed C<-1> and C<0> above; I can fix those up, upon pushing, in case no more changes were requested. Laszlo > +static int > +launch_passt (guestfs_h *g, long *passt_pid, char (*sockpath)[UNIX_PATH_MAX]) > +{ > + int rc; > + char sockpath_local[sizeof *sockpath]; > + char *pid_path; > + struct command *cmd; > + int passt_status; > + int passt_exit; > + char *pid_str; > + long passt_pid_local; > + char *endptr; > + > + rc = -1; > + if (guestfs_int_create_socketname (g, "passt.sock", &sockpath_local) == -1) > + return rc; > + > + pid_path = guestfs_int_make_pid_path (g, "passt"); > + if (pid_path == NULL) > + return rc; > + > + cmd = guestfs_int_new_command (g); > + if (cmd == NULL) > + goto free_pid_path; > + > + guestfs_int_cmd_add_arg (cmd, "passt"); > + guestfs_int_cmd_add_arg (cmd, "--one-off"); > + guestfs_int_cmd_add_arg (cmd, "--socket"); > + guestfs_int_cmd_add_arg (cmd, sockpath_local); > + guestfs_int_cmd_add_arg (cmd, "--pid"); > + guestfs_int_cmd_add_arg (cmd, pid_path); > + guestfs_int_cmd_add_arg (cmd, "--address"); > + guestfs_int_cmd_add_arg (cmd, NETWORK_ADDRESS); > + guestfs_int_cmd_add_arg (cmd, "--netmask"); > + guestfs_int_cmd_add_arg (cmd, NETWORK_PREFIX); > + guestfs_int_cmd_add_arg (cmd, "--mac-addr"); > + guestfs_int_cmd_add_arg (cmd, NETWORK_GW_MAC); > + guestfs_int_cmd_add_arg (cmd, "--gateway"); > + guestfs_int_cmd_add_arg (cmd, NETWORK_GW_IP); > + > + passt_status = guestfs_int_cmd_run (cmd); > + if (passt_status == -1) > + /* guestfs_int_cmd_run() reports errors internally, so just bail here */ > + goto close_cmd; > + > + if (WIFSIGNALED (passt_status)) { > + error (g, _("passt was killed with signal %d"), WTERMSIG (passt_status)); > + goto close_cmd; > + } > + > + assert (WIFEXITED (passt_status)); > + passt_exit = WEXITSTATUS (passt_status); > + if (passt_exit != 0) { > + error (g, _("passt exited with status %d"), passt_exit); > + goto close_cmd; > + } > + > + /* At this point passt has forked into the background, dropped privileges, > and > + * written a PID file. Due to "--one-off", passt will exit once our QEMU > + * appliance disappears (forcibly or cleanly); however, we still need the > + * passt PID *temporarily*, so we can kill passt in case we encounter an > error > + * *before* starting the appliance. > + */ > + if (guestfs_int_read_whole_file (g, pid_path, &pid_str, NULL) == -1) > + /* Any error has been reported internally, so just bail. We can't kill > + * passt here because we've failed to get its PID in the first place... > + */ > + goto close_cmd; > + > + errno = 0; > + passt_pid_local = strtol (pid_str, &endptr, 10); > + if (endptr == pid_str || (*endptr != '\0' && *endptr != '\n') || errno != > 0 || > + passt_pid_local <= 1) { > + /* Same thing, we can't kill passt just yet. */ > + error (g, _("failed to parse passt PID from '%s'"), pid_path); > + goto free_pid_str; > + } > + > + /* We're done. */ > + *passt_pid = passt_pid_local; > + ignore_value (strcpy (*sockpath, sockpath_local)); > + rc = 0; > + > +free_pid_str: > + free (pid_str); > + > +close_cmd: > + guestfs_int_cmd_close (cmd); > + > +free_pid_path: > + free (pid_path); > + > + return rc; > +} > + > static int > launch_direct (guestfs_h *g, void *datav, const char *arg) > { > @@ -340,6 +446,7 @@ launch_direct (guestfs_h *g, void *datav, const char *arg) > struct qemuopts *qopts = NULL; > int daemon_accept_sock = -1, console_sock = -1; > int r; > + long passt_pid = -1; > int flags; > int sv[2]; > struct sockaddr_un addr; > @@ -658,11 +765,30 @@ launch_direct (guestfs_h *g, void *datav, const char > *arg) > > /* Enable user networking. */ > if (g->enable_network) { > - start_list ("-netdev") { > - append_list ("user"); > - append_list ("id=usernet"); > - append_list ("net=" NETWORK_ADDRESS "/" NETWORK_PREFIX); > - } end_list (); > + /* If qemu is 7.2.0+ and "passt" is available, ask for passt rather > + * than SLIRP. RHBZ#2184967. > + */ > + if (guestfs_int_version_ge (&data->qemu_version, 7, 2, 0) && > + guestfs_int_passt_runnable (g)) { > + char passt_sock[UNIX_PATH_MAX]; > + > + if (launch_passt (g, &passt_pid, &passt_sock) == -1) > + goto cleanup0; > + > + start_list ("-netdev") { > + append_list ("stream"); > + append_list ("id=usernet"); > + append_list ("addr.type=unix"); > + append_list_format ("addr.path=%s", passt_sock); > + } end_list (); > + } > + else { > + start_list ("-netdev") { > + append_list ("user"); > + append_list ("id=usernet"); > + append_list ("net=" NETWORK_ADDRESS "/" NETWORK_PREFIX); > + } end_list (); > + } > start_list ("-device") { > append_list (VIRTIO_DEVICE_NAME ("virtio-net")); > append_list ("netdev=usernet"); > @@ -893,6 +1019,15 @@ launch_direct (guestfs_h *g, void *datav, const char > *arg) > > debug (g, "appliance is up"); > > + /* From this point onward, even if we fail, QEMU terminating (forcefully or > + * gracefully) will cause passt to go away as well. Note that we can't > + * precisely tell whether QEMU managed to open the passt socket before QEMU > + * failed. Therefore, err on the side of killing passt needlessly, rather > + * than not killing it when needed -- that's why we re-set "passt_pid" to > (-1) > + * only this late during QEMU startup verification. > + */ > + passt_pid = -1; > + > /* This is possible in some really strange situations, such as > * guestfsd starts up OK but then qemu immediately exits. Check for > * it because the caller is probably expecting to be able to send > @@ -924,6 +1059,8 @@ launch_direct (guestfs_h *g, void *datav, const char > *arg) > data->qemu_data = NULL; > > cleanup0: > + if (passt_pid != -1) > + kill (passt_pid, SIGTERM); > if (qopts != NULL) > qemuopts_free (qopts); > if (daemon_accept_sock >= 0) > _______________________________________________ > Libguestfs mailing list > Libguestfs@redhat.com > https://listman.redhat.com/mailman/listinfo/libguestfs > _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs