1
0
mirror of https://github.com/systemd/systemd synced 2024-07-09 04:26:06 +00:00

Merge pull request #32249 from CodethinkLabs/vmspawn/predicatable_tap_names

vmspawn: generate predicatable TAP device names and MAC addresses
This commit is contained in:
Yu Watanabe 2024-04-18 10:26:07 +09:00 committed by GitHub
commit d2a0ef6073
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 160 additions and 94 deletions

View 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

View File

@ -24,6 +24,7 @@
#include "mount-util.h"
#include "namespace-util.h"
#include "netif-naming-scheme.h"
#include "netif-util.h"
#include "netlink-util.h"
#include "nspawn-network.h"
#include "parse-util.h"
@ -41,7 +42,6 @@
#define VETH_EXTRA_HOST_HASH_KEY SD_ID128_MAKE(48,c7,f6,b7,ea,9d,4c,9e,b7,28,d4,de,91,d5,bf,66)
#define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59)
#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f)
#define SHORTEN_IFNAME_HASH_KEY SD_ID128_MAKE(e1,90,a4,04,a8,ef,4b,51,8c,cc,c3,3a,9f,11,fc,a2)
static int remove_one_link(sd_netlink *rtnl, const char *name) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
@ -67,49 +67,6 @@ static int remove_one_link(sd_netlink *rtnl, const char *name) {
return 1;
}
static int generate_mac(
const char *machine_name,
struct ether_addr *mac,
sd_id128_t hash_key,
uint64_t idx) {
uint64_t result;
size_t l, sz;
uint8_t *v, *i;
int r;
l = strlen(machine_name);
sz = sizeof(sd_id128_t) + l;
if (idx > 0)
sz += sizeof(idx);
v = newa(uint8_t, sz);
/* fetch some persistent data unique to the host */
r = sd_id128_get_machine((sd_id128_t*) v);
if (r < 0)
return r;
/* combine with some data unique (on this host) to this
* container instance */
i = mempcpy(v + sizeof(sd_id128_t), machine_name, l);
if (idx > 0) {
idx = htole64(idx);
memcpy(i, &idx, sizeof(idx));
}
/* Let's hash the host machine ID plus the container name. We
* use a fixed, but originally randomly created hash key here. */
result = htole64(siphash24(v, sz, hash_key.bytes));
assert_cc(ETH_ALEN <= sizeof(result));
memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
ether_addr_mark_random(mac);
return 0;
}
static int set_alternative_ifname(sd_netlink *rtnl, const char *ifname, const char *altifname) {
int r;
@ -208,39 +165,6 @@ static int add_veth(
return 0;
}
static int shorten_ifname(char *ifname) {
char new_ifname[IFNAMSIZ];
assert(ifname);
if (strlen(ifname) < IFNAMSIZ) /* Name is short enough */
return 0;
if (naming_scheme_has(NAMING_NSPAWN_LONG_HASH)) {
uint64_t h;
/* Calculate 64-bit hash value */
h = siphash24(ifname, strlen(ifname), SHORTEN_IFNAME_HASH_KEY.bytes);
/* Set the final four bytes (i.e. 32-bit) to the lower 24bit of the hash, encoded in url-safe base64 */
memcpy(new_ifname, ifname, IFNAMSIZ - 5);
new_ifname[IFNAMSIZ - 5] = urlsafe_base64char(h >> 18);
new_ifname[IFNAMSIZ - 4] = urlsafe_base64char(h >> 12);
new_ifname[IFNAMSIZ - 3] = urlsafe_base64char(h >> 6);
new_ifname[IFNAMSIZ - 2] = urlsafe_base64char(h);
} else
/* On old nspawn versions we just truncated the name, provide compatibility */
memcpy(new_ifname, ifname, IFNAMSIZ-1);
new_ifname[IFNAMSIZ - 1] = 0;
/* Log the incident to make it more discoverable */
log_warning("Network interface name '%s' has been changed to '%s' to fit length constraints.", ifname, new_ifname);
strcpy(ifname, new_ifname);
return 1;
}
int setup_veth(const char *machine_name,
pid_t pid,
char iface_name[IFNAMSIZ],
@ -260,18 +184,18 @@ int setup_veth(const char *machine_name,
/* Use two different interface name prefixes depending whether
* we are in bridge mode or not. */
n = strjoina(bridge ? "vb-" : "ve-", machine_name);
r = shorten_ifname(n);
r = net_shorten_ifname(n, /* check_naming_scheme= */ true);
if (r > 0)
a = strjoina(bridge ? "vb-" : "ve-", machine_name);
if (ether_addr_is_null(provided_mac)){
r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
r = net_generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0);
if (r < 0)
return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m");
} else
mac_container = *provided_mac;
r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
r = net_generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0);
if (r < 0)
return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m");
@ -314,11 +238,11 @@ int setup_veth_extra(
STRV_FOREACH_PAIR(a, b, pairs) {
struct ether_addr mac_host, mac_container;
r = generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx);
r = net_generate_mac(machine_name, &mac_container, VETH_EXTRA_CONTAINER_HASH_KEY, idx);
if (r < 0)
return log_error_errno(r, "Failed to generate predictable MAC address for container side of extra veth link: %m");
r = generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx);
r = net_generate_mac(machine_name, &mac_host, VETH_EXTRA_HOST_HASH_KEY, idx);
if (r < 0)
return log_error_errno(r, "Failed to generate predictable MAC address for host side of extra veth link: %m");
@ -836,7 +760,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **iface_pairs) {
if (ifi < 0)
return ifi;
r = generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++);
r = net_generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++);
if (r < 0)
return log_error_errno(r, "Failed to create MACVLAN MAC address: %m");
@ -852,7 +776,7 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **iface_pairs) {
if (!n)
return log_oom();
shortened = shorten_ifname(n);
shortened = net_shorten_ifname(n, /* check_naming_scheme= */ true);
r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)
@ -929,7 +853,7 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **iface_pairs) {
if (!n)
return log_oom();
shortened = shorten_ifname(n);
shortened = net_shorten_ifname(n, /* check_naming_scheme= */ true);
r = sd_netlink_message_append_string(m, IFLA_IFNAME, n);
if (r < 0)

View File

@ -5,13 +5,17 @@
#include "arphrd-util.h"
#include "device-util.h"
#include "hexdecoct.h"
#include "log-link.h"
#include "memory-util.h"
#include "netif-naming-scheme.h"
#include "netif-util.h"
#include "siphash24.h"
#include "sparse-endian.h"
#include "strv.h"
#define SHORTEN_IFNAME_HASH_KEY SD_ID128_MAKE(e1,90,a4,04,a8,ef,4b,51,8c,cc,c3,3a,9f,11,fc,a2)
bool netif_has_carrier(uint8_t operstate, unsigned flags) {
/* see Documentation/networking/operstates.txt in the kernel sources */
@ -198,3 +202,79 @@ int net_verify_hardware_address(
return 0;
}
int net_generate_mac(
const char *machine_name,
struct ether_addr *mac,
sd_id128_t hash_key,
uint64_t idx) {
uint64_t result;
size_t l, sz;
uint8_t *v, *i;
int r;
l = strlen(machine_name);
sz = sizeof(sd_id128_t) + l;
if (idx > 0)
sz += sizeof(idx);
v = newa(uint8_t, sz);
/* fetch some persistent data unique to the host */
r = sd_id128_get_machine((sd_id128_t*) v);
if (r < 0)
return r;
/* combine with some data unique (on this host) to this
* container instance */
i = mempcpy(v + sizeof(sd_id128_t), machine_name, l);
if (idx > 0) {
idx = htole64(idx);
memcpy(i, &idx, sizeof(idx));
}
/* Let's hash the host machine ID plus the container name. We
* use a fixed, but originally randomly created hash key here. */
result = htole64(siphash24(v, sz, hash_key.bytes));
assert_cc(ETH_ALEN <= sizeof(result));
memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
ether_addr_mark_random(mac);
return 0;
}
int net_shorten_ifname(char *ifname, bool check_naming_scheme) {
char new_ifname[IFNAMSIZ];
assert(ifname);
if (strlen(ifname) < IFNAMSIZ) /* Name is short enough */
return 0;
if (!check_naming_scheme || naming_scheme_has(NAMING_NSPAWN_LONG_HASH)) {
uint64_t h;
/* Calculate 64-bit hash value */
h = siphash24(ifname, strlen(ifname), SHORTEN_IFNAME_HASH_KEY.bytes);
/* Set the final four bytes (i.e. 32-bit) to the lower 24bit of the hash, encoded in url-safe base64 */
memcpy(new_ifname, ifname, IFNAMSIZ - 5);
new_ifname[IFNAMSIZ - 5] = urlsafe_base64char(h >> 18);
new_ifname[IFNAMSIZ - 4] = urlsafe_base64char(h >> 12);
new_ifname[IFNAMSIZ - 3] = urlsafe_base64char(h >> 6);
new_ifname[IFNAMSIZ - 2] = urlsafe_base64char(h);
} else
/* On old nspawn versions we just truncated the name, provide compatibility */
memcpy(new_ifname, ifname, IFNAMSIZ-1);
new_ifname[IFNAMSIZ - 1] = 0;
/* Log the incident to make it more discoverable */
log_warning("Network interface name '%s' has been changed to '%s' to fit length constraints.", ifname, new_ifname);
strcpy(ifname, new_ifname);
return 1;
}

View File

@ -20,3 +20,9 @@ int net_verify_hardware_address(
uint16_t iftype,
const struct hw_addr_data *ib_hw_addr,
struct hw_addr_data *new_hw_addr);
int net_generate_mac(
const char *machine_name,
struct ether_addr *mac,
sd_id128_t hash_key,
uint64_t idx);
int net_shorten_ifname(char *ifname, bool check_naming_scheme);

View 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>
@ -8,26 +10,26 @@
#include <sys/stat.h>
#include <unistd.h>
#include "bootspec.h"
#include "chase.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "discover-image.h"
#include "pidref.h"
#include "sd-daemon.h"
#include "sd-event.h"
#include "sd-id128.h"
#include "alloc-util.h"
#include "architecture.h"
#include "bootspec.h"
#include "build.h"
#include "chase.h"
#include "common-signal.h"
#include "copy.h"
#include "creds-util.h"
#include "dirent-util.h"
#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"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
@ -41,11 +43,13 @@
#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"
#include "path-lookup.h"
#include "path-util.h"
#include "pidref.h"
#include "pretty-print.h"
#include "process-util.h"
#include "ptyfwd.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,
@ -1133,6 +1154,7 @@ static void set_window_title(PTYForward *f) {
if (dot)
(void) pty_forward_set_title_prefix(f, dot);
}
static int generate_ssh_keypair(const char *key_path, const char *key_type) {
_cleanup_free_ char *ssh_keygen = NULL;
_cleanup_strv_free_ char **cmdline = NULL;
@ -1286,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");
@ -2013,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;