]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: generate predicatable TAP device names and MAC addresses 32249/head
authorSam Leonard <sam.leonard@codethink.co.uk>
Fri, 12 Apr 2024 14:46:04 +0000 (15:46 +0100)
committerSam Leonard <sam.leonard@codethink.co.uk>
Mon, 15 Apr 2024 10:42:06 +0000 (11:42 +0100)
docs/ENVIRONMENT.md
src/vmspawn/vmspawn.c

index 3585ab012359787123529fd476d85eb235cbd90c..961601c72e0dc76f3215d489667a65ff02d00a1d 100644 (file)
@@ -183,6 +183,14 @@ All tools:
   expected format is six groups of two hexadecimal digits separated by colons,
   e.g. `SYSTEMD_NSPAWN_NETWORK_MAC=12:34:56:78:90:AB`
 
+`systemd-vmspawn`:
+
+* `$SYSTEMD_VMSPAWN_NETWORK_MAC=...` — if set, allows users to set a specific MAC
+  address for a VM, ensuring that it uses the provided value instead of
+  generating a random one. It is effective when used with `--network-tap`. The
+  expected format is six groups of two hexadecimal digits separated by colons,
+  e.g. `SYSTEMD_VMSPAWN_NETWORK_MAC=12:34:56:78:90:AB`
+
 `systemd-logind`:
 
 * `$SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1` — if set, report that
index fea154ae1f17fa2c5ee457fe5ef3a2eafc339cd8..0eba4569715140e207f4d32e7ad95c87b5243279 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <net/if.h>
+#include <linux/if.h>
 #include <getopt.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -24,6 +26,7 @@
 #include "discover-image.h"
 #include "dissect-image.h"
 #include "escape.h"
+#include "ether-addr-util.h"
 #include "event-util.h"
 #include "extract-word.h"
 #include "fd-util.h"
@@ -40,6 +43,7 @@
 #include "macro.h"
 #include "main-func.h"
 #include "mkdir.h"
+#include "netif-util.h"
 #include "pager.h"
 #include "parse-argument.h"
 #include "parse-util.h"
@@ -65,6 +69,8 @@
 #include "vmspawn-settings.h"
 #include "vmspawn-util.h"
 
+#define VM_TAP_HASH_KEY SD_ID128_MAKE(01,d0,c6,4c,2b,df,24,fb,c0,f8,b2,09,7d,59,b2,93)
+
 static bool arg_quiet = false;
 static PagerFlags arg_pager_flags = 0;
 static char *arg_directory = NULL;
@@ -98,6 +104,7 @@ static char *arg_background = NULL;
 static bool arg_pass_ssh_key = true;
 static char *arg_ssh_key_type = NULL;
 static bool arg_discard_disk = true;
+struct ether_addr arg_network_provided_mac = {};
 
 STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
@@ -188,6 +195,20 @@ static int help(void) {
         return 0;
 }
 
+static int parse_environment(void) {
+        const char *e;
+        int r;
+
+        e = getenv("SYSTEMD_VMSPAWN_NETWORK_MAC");
+        if (e) {
+                r = parse_ether_addr(e, &arg_network_provided_mac);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to parse provided MAC address via environment variable");
+        }
+
+        return 0;
+}
+
 static int parse_argv(int argc, char *argv[]) {
         enum {
                 ARG_VERSION = 0x100,
@@ -1287,9 +1308,31 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
                 }
         }
 
-        if (arg_network_stack == NETWORK_STACK_TAP)
-                r = strv_extend_many(&cmdline, "-nic", "tap,script=no,model=virtio-net-pci");
-        else if (arg_network_stack == NETWORK_STACK_USER)
+        if (arg_network_stack == NETWORK_STACK_TAP) {
+                _cleanup_free_ char *tap_name = NULL;
+                struct ether_addr mac_vm = {};
+
+                tap_name = strjoin("tp-", 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)){
+                        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 = strv_extend(&cmdline, "-nic");
+                if (r < 0)
+                        return log_oom();
+
+                r = strv_extendf(&cmdline, "tap,ifname=%s,script=no,model=virtio-net-pci,mac=%s", tap_name, ETHER_ADDR_TO_STR(&mac_vm));
+                if (r < 0)
+                        return log_oom();
+        } else if (arg_network_stack == NETWORK_STACK_USER)
                 r = strv_extend_many(&cmdline, "-nic", "user,model=virtio-net-pci");
         else
                 r = strv_extend_many(&cmdline, "-nic", "none");
@@ -2014,6 +2057,10 @@ static int run(int argc, char *argv[]) {
         /* don't attempt to register as a machine when running as a user */
         arg_register = arg_privileged;
 
+        r = parse_environment();
+        if (r < 0)
+                return r;
+
         r = parse_argv(argc, argv);
         if (r <= 0)
                 return r;