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