]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: convert network device setup to bridge
authorChristian Brauner <brauner@kernel.org>
Tue, 7 Apr 2026 12:47:10 +0000 (14:47 +0200)
committerChristian Brauner <brauner@kernel.org>
Wed, 15 Apr 2026 08:14:48 +0000 (10:14 +0200)
Remove the static netdev/nic INI config sections for both privileged
TAP, nsresourced TAP, and user-mode networking. Replace them with a
NetworkInfo struct that captures the network type, TAP fd or interface
name, and MAC address, passed to vmspawn_varlink_setup_network() for
runtime configuration via QMP.

For the nsresourced TAP path the fd is now passed to QEMU via QMP
getfd + SCM_RIGHTS instead of being inherited through pass_fds.

Declare the MachineConfig aggregate that this and the following
conversion patches populate, zero-initialized with explicit -EBADF
for the fd fields so every sub-structure cleans up safely regardless
of which device types the invocation ends up using.

Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
src/vmspawn/vmspawn.c

index 8cf2f9112938b464265b49fd0d511982731624e1..2120df61af970cfbc32f3b46fce4cb005861223a 100644 (file)
@@ -2335,6 +2335,10 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         _cleanup_close_ int notify_sock_fd = -EBADF;
         _cleanup_strv_free_ char **cmdline = NULL;
         _cleanup_free_ int *pass_fds = NULL;
+        _cleanup_(machine_config_done) MachineConfig config = {
+                .network = { .fd = -EBADF },
+                .vsock   = { .fd = -EBADF },
+        };
         sd_event_source **children = NULL;
         size_t n_children = 0, n_pass_fds = 0;
         int r;
@@ -2573,8 +2577,14 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                         return log_oom();
 
         _cleanup_close_ int delegate_userns_fd = -EBADF, tap_fd = -EBADF;
+        _cleanup_free_ char *tap_name = NULL;
+        struct ether_addr mac_vm = {};
+
         if (arg_network_stack == NETWORK_STACK_TAP) {
                 if (have_effective_cap(CAP_NET_ADMIN) <= 0) {
+                        /* Without CAP_NET_ADMIN we use nsresourced to create a TAP device.
+                         * The TAP fd is passed to QEMU via QMP getfd + SCM_RIGHTS after
+                         * the handshake, then referenced by name in netdev_add. */
                         delegate_userns_fd = userns_acquire_self_root();
                         if (delegate_userns_fd < 0)
                                 return log_error_errno(delegate_userns_fd, "Failed to acquire userns: %m");
@@ -2596,65 +2606,39 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                         if (tap_fd < 0)
                                 return log_error_errno(tap_fd, "Failed to allocate network tap device: %m");
 
-                        r = strv_extend(&cmdline, "-netdev");
-                        if (r < 0)
-                                return log_oom();
-
-                        r = strv_extendf(&cmdline, "tap,id=net0,fd=%i", tap_fd);
-                        if (r < 0)
-                                return log_oom();
-
-                        r = strv_extend_many(&cmdline, "-device", "virtio-net-pci,netdev=net0");
-                        if (r < 0)
-                                return log_oom();
-
-                        if (!GREEDY_REALLOC(pass_fds, n_pass_fds + 1))
-                                return log_oom();
-
-                        pass_fds[n_pass_fds++] = tap_fd;
+                        config.network = (NetworkInfo) {
+                                .type = "tap",
+                                .fd   = TAKE_FD(tap_fd),
+                        };
                 } else {
-                        _cleanup_free_ char *tap_name = NULL;
-                        struct ether_addr mac_vm = {};
-
+                        /* With CAP_NET_ADMIN we create the TAP interface by name.
+                         * Configure via QMP after QEMU starts. */
                         tap_name = strjoin("vt-", arg_machine);
                         if (!tap_name)
                                 return log_oom();
 
                         (void) net_shorten_ifname(tap_name, /* check_naming_scheme= */ false);
 
-                        if (ether_addr_is_null(&arg_network_provided_mac)){
+                        if (ether_addr_is_null(&arg_network_provided_mac)) {
                                 r = net_generate_mac(arg_machine, &mac_vm, VM_TAP_HASH_KEY, 0);
                                 if (r < 0)
                                         return log_error_errno(r, "Failed to generate predictable MAC address for VM side: %m");
                         } else
                                 mac_vm = arg_network_provided_mac;
 
-                        r = qemu_config_section(config_file, "netdev", "net0",
-                                                "type", "tap",
-                                                "ifname", tap_name,
-                                                "script", "no",
-                                                "downscript", "no");
-                        if (r < 0)
-                                return r;
-
-                        r = qemu_config_section(config_file, "device", "nic0",
-                                                "driver", "virtio-net-pci",
-                                                "netdev", "net0",
-                                                "mac", ETHER_ADDR_TO_STR(&mac_vm));
-                        if (r < 0)
-                                return r;
+                        config.network = (NetworkInfo) {
+                                .type    = "tap",
+                                .ifname  = TAKE_PTR(tap_name),
+                                .mac     = mac_vm,
+                                .mac_set = true,
+                                .fd      = -EBADF,
+                        };
                 }
         } else if (arg_network_stack == NETWORK_STACK_USER) {
-                r = qemu_config_section(config_file, "netdev", "net0",
-                                        "type", "user");
-                if (r < 0)
-                        return r;
-
-                r = qemu_config_section(config_file, "device", "nic0",
-                                        "driver", "virtio-net-pci",
-                                        "netdev", "net0");
-                if (r < 0)
-                        return r;
+                config.network = (NetworkInfo) {
+                        .type = "user",
+                        .fd   = -EBADF,
+                };
         } else {
                 r = strv_extend_many(&cmdline, "-nic", "none");
                 if (r < 0)
@@ -3621,6 +3605,13 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
         if (r < 0)
                 return r;
 
+        /* Device setup — all before resuming vCPUs */
+        if (config.network.type) {
+                r = vmspawn_qmp_setup_network(bridge, &config.network);
+                if (r < 0)
+                        return r;
+        }
+
         /* Resume vCPUs and switch to async event processing */
         r = vmspawn_qmp_start(bridge);
         if (r < 0)