diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index a9011d9106..38f5e3c93e 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -176,6 +176,31 @@ + + + + + + Create a TAP device to network with the virtual machine. + + Note: root privileges are required to use TAP networking. + Additionally requires a correctly setup + systemd-networkd8 + to be running on the host to ensure the host interface is correctly configured. + The relevant .network file can be found at /usr/lib/systemd/network/80-vm-vt.network. + + + + + + + + + Use user mode networking with QEMU. + + + + PATH diff --git a/src/vmspawn/vmspawn-util.c b/src/vmspawn/vmspawn-util.c index 79e1e5399a..822b102912 100644 --- a/src/vmspawn/vmspawn-util.c +++ b/src/vmspawn/vmspawn-util.c @@ -20,6 +20,7 @@ #include "siphash24.h" #include "socket-util.h" #include "sort-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "vmspawn-util.h" @@ -35,6 +36,8 @@ OvmfConfig* ovmf_config_free(OvmfConfig *config) { return mfree(config); } +DEFINE_STRING_TABLE_LOOKUP(qemu_network_stack, QemuNetworkStack); + int qemu_check_kvm_support(void) { if (access("/dev/kvm", F_OK) >= 0) return true; diff --git a/src/vmspawn/vmspawn-util.h b/src/vmspawn/vmspawn-util.h index c3dbdf205b..9c9b1867d3 100644 --- a/src/vmspawn/vmspawn-util.h +++ b/src/vmspawn/vmspawn-util.h @@ -45,6 +45,23 @@ static inline const char *ovmf_config_vars_format(const OvmfConfig *c) { OvmfConfig* ovmf_config_free(OvmfConfig *ovmf_config); DEFINE_TRIVIAL_CLEANUP_FUNC(OvmfConfig*, ovmf_config_free); +typedef enum QemuNetworkStack { + QEMU_NET_TAP, + QEMU_NET_USER, + QEMU_NET_NONE, + _QEMU_NET_MAX, + _QEMU_NET_INVALID = -EINVAL, +} QemuNetworkStack; + +static const char* const qemu_network_stack_table[_QEMU_NET_MAX] = { + [QEMU_NET_TAP] = "tap", + [QEMU_NET_USER] = "user", + [QEMU_NET_NONE] = "none", +}; + +const char* qemu_network_stack_to_string(QemuNetworkStack type) _const_; +QemuNetworkStack qemu_network_stack_from_string(const char *s) _pure_; + int qemu_check_kvm_support(void); int qemu_check_vsock_support(void); int list_ovmf_config(char ***ret); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index 5e5139d574..37c354f0b0 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -61,6 +61,7 @@ static int arg_tpm = -1; static char *arg_linux = NULL; static char *arg_initrd = NULL; static bool arg_qemu_gui = false; +static QemuNetworkStack arg_network_stack = QEMU_NET_NONE; static int arg_secure_boot = -1; static MachineCredentialContext arg_credentials = {}; static SettingsMask arg_settings_mask = 0; @@ -109,6 +110,8 @@ static int help(void) { " --linux=PATH Specify the linux kernel for direct kernel boot\n" " --initrd=PATH Specify the initrd for direct kernel boot\n" " --qemu-gui Start QEMU in graphical mode\n" + " -n --network-tap Create a TAP device for networking with QEMU.\n" + " --network-user-mode Use user mode networking with QEMU.\n" " --secure-boot=BOOL Configure whether to search for firmware which\n" " supports Secure Boot\n" " --firmware=PATH|list Select firmware definition file (or list available)\n" @@ -145,6 +148,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_LINUX, ARG_INITRD, ARG_QEMU_GUI, + ARG_NETWORK_USER_MODE, ARG_SECURE_BOOT, ARG_SET_CREDENTIAL, ARG_LOAD_CREDENTIAL, @@ -152,25 +156,27 @@ static int parse_argv(int argc, char *argv[]) { }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "quiet", no_argument, NULL, 'q' }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "image", required_argument, NULL, 'i' }, - { "machine", required_argument, NULL, 'M' }, - { "qemu-smp", required_argument, NULL, ARG_QEMU_SMP }, - { "qemu-mem", required_argument, NULL, ARG_QEMU_MEM }, - { "qemu-kvm", required_argument, NULL, ARG_QEMU_KVM }, - { "qemu-vsock", required_argument, NULL, ARG_QEMU_VSOCK }, - { "vsock-cid", required_argument, NULL, ARG_VSOCK_CID }, - { "tpm", required_argument, NULL, ARG_TPM }, - { "linux", required_argument, NULL, ARG_LINUX }, - { "initrd", required_argument, NULL, ARG_INITRD }, - { "qemu-gui", no_argument, NULL, ARG_QEMU_GUI }, - { "secure-boot", required_argument, NULL, ARG_SECURE_BOOT }, - { "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL }, - { "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL }, - { "firmware", required_argument, NULL, ARG_FIRMWARE }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "quiet", no_argument, NULL, 'q' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "image", required_argument, NULL, 'i' }, + { "machine", required_argument, NULL, 'M' }, + { "qemu-smp", required_argument, NULL, ARG_QEMU_SMP }, + { "qemu-mem", required_argument, NULL, ARG_QEMU_MEM }, + { "qemu-kvm", required_argument, NULL, ARG_QEMU_KVM }, + { "qemu-vsock", required_argument, NULL, ARG_QEMU_VSOCK }, + { "vsock-cid", required_argument, NULL, ARG_VSOCK_CID }, + { "tpm", required_argument, NULL, ARG_TPM }, + { "linux", required_argument, NULL, ARG_LINUX }, + { "initrd", required_argument, NULL, ARG_INITRD }, + { "qemu-gui", no_argument, NULL, ARG_QEMU_GUI }, + { "network-tap", no_argument, NULL, 'n' }, + { "network-user-mode", no_argument, NULL, ARG_NETWORK_USER_MODE }, + { "secure-boot", required_argument, NULL, ARG_SECURE_BOOT }, + { "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL }, + { "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL }, + { "firmware", required_argument, NULL, ARG_FIRMWARE }, {} }; @@ -180,7 +186,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argv); optind = 0; - while ((c = getopt_long(argc, argv, "+hi:M:q", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hi:M:nq", options, NULL)) >= 0) switch (c) { case 'h': return help(); @@ -281,6 +287,14 @@ static int parse_argv(int argc, char *argv[]) { arg_qemu_gui = true; break; + case 'n': + arg_network_stack = QEMU_NET_TAP; + break; + + case ARG_NETWORK_USER_MODE: + arg_network_stack = QEMU_NET_USER; + break; + case ARG_SECURE_BOOT: r = parse_tristate(optarg, &arg_secure_boot); if (r < 0) @@ -702,8 +716,7 @@ static int run_virtual_machine(void) { "-smp", arg_qemu_smp ?: "1", "-m", mem, "-object", "rng-random,filename=/dev/urandom,id=rng0", - "-device", "virtio-rng-pci,rng=rng0,id=rng-device0", - "-nic", "user,model=virtio-net-pci" + "-device", "virtio-rng-pci,rng=rng0,id=rng-device0" ); if (!cmdline) return log_oom(); @@ -722,6 +735,15 @@ static int run_virtual_machine(void) { } } + if (arg_network_stack == QEMU_NET_TAP) + r = strv_extend_many(&cmdline, "-nic", "tap,script=no,model=virtio-net-pci"); + else if (arg_network_stack == QEMU_NET_USER) + r = strv_extend_many(&cmdline, "-nic", "user,model=virtio-net-pci"); + else + r = strv_extend_many(&cmdline, "-nic", "none"); + if (r < 0) + return log_oom(); + bool use_vsock = arg_qemu_vsock > 0 && ARCHITECTURE_SUPPORTS_SMBIOS; if (arg_qemu_vsock < 0) { r = qemu_check_vsock_support(); @@ -1062,6 +1084,13 @@ static int determine_names(void) { return 0; } +static int verify_arguments(void) { + if (arg_network_stack == QEMU_NET_TAP && !arg_privileged) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "--network-tap requires root privileges, refusing."); + + return 0; +} + static int run(int argc, char *argv[]) { int r; @@ -1077,6 +1106,10 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; + r = verify_arguments(); + if (r < 0) + return r; + if (!arg_quiet) { _cleanup_free_ char *u = NULL; (void) terminal_urlify_path(arg_image, arg_image, &u);