From 6d36464065601f79a352367cf099be8907d8f9aa Mon Sep 17 00:00:00 2001 From: =?utf8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 10 Jan 2019 16:23:49 +0100 Subject: [PATCH] udev,networkd: use the interface name as fallback basis for MAC and IPv4LL seed Fixes #3374. The problem is that we set MACPolicy=persistent (i.e. we would like to generate persistent MAC addresses for interfaces which don't have a fixed MAC address), but various virtual interfaces including bridges, tun/tap, bonds, etc., do not not have the necessary ID_NET_NAME_* attributes and udev would not assing the address and warn: Could not generate persistent MAC address for $name: No such file or directory Basic requirements which I think a solution for this needs to satisfy: 1. No changes to MAC address generation for those cases which are currently handled successfully. This means that net_get_unique_predictable_data() must keep returning the same answer, which in turn means net_get_name() must keep returning the same answer. We can only add more things we look at with lower priority so that we start to cover cases which were not covered before. 2. Like 1, but for IPvLL seed and DHCP IAD. This is less important, but "nice to have". 3. Keep MACPolicy=persistent. If people don't want it, they can always apply local configuration, but in general stable MACs are a good thing. I have never seen anyone complain about that. == Various approaches that have been proposed === https://github.com/systemd/systemd/issues/3374#issuecomment-223753264 (tomty89) if !ID_BUS and INTERFACE, use INTERFACE I think this almost does the good thing, but I don't see the reason to reject ID_BUS (i.e. physical hardware). Stable MACs are very useful for physical hardware that has no physical MAC. === https://github.com/systemd/systemd/issues/3374#issuecomment-224733069 (teg) if (should_rename(device, true)) This means looking at name_assign_type. In particular for NET_NAME_USER should_rename(..., true) returns true. It only returns false for NET_NAME_PREDICTABLE. So this would cover stuff like br0, bond0, etc, but would not cover lo and other devices with predictable names. That doesn't make much sense. But did teg mean should_rename() or !should_rename()? === https://github.com/systemd/systemd/issues/3374#issuecomment-234628502 (tomty89): + if (!should_rename(device, true)) + return udev_device_get_sysname(device) This covers only devices with NET_NAME_PREDICTABLE. Since the problem applies as much to bridges and such, this isn't neough. === https://github.com/systemd/systemd/issues/3374#issuecomment-281745967 (grafi-tt) + /* if the machine doesn't provide data about the device, use the ifname specified by userspace + * (this is the case when the device is virtual, e.g., bridge or bond) */ + s = udev_device_get_sysattr_value(device, "name_assign_type"); + if (s && safe_atou(s, &type) >= 0 && type == NET_NAME_USER) + return udev_device_get_sysname(device); This does not cover bond0, vnet0, tun/tap and similar. grafi-tt also proposes patching the kernel, but *not* setting name_assign_type seems intentional in those cases, because the device name is a result of enumeration, not set by the userspace. === https://github.com/systemd/systemd/issues/3374#issuecomment-288882355 (tomty89) (also PR #11372) - MACAddressPolicy=persistent This break requirement 3. above. It would solve the immediate problem, but I think the disruption is too big. === This patch This patch means that we will set a "stable" MAC for pretty much any virtual device by default, where "stable" means keyed off the machine-id and interface name. It seems like a big change, but we already did this for most physical devices. Doing it also for virtual devices doesn't seem like a big issue. It will make the setup and monitoring of virtualized networks slightly nicer. I don't think anyone is depending on having the MAC address changed when those devices are destoryed and recreated. If they do, they'd have to change MACAddressPolicy=. == Implementation net_get_name() is called from dhcp_ident_set_iaid() so I didn't change net_get_name() like in grafi-tt's patch, but net_get_unique_predictable_data(). net_get_unique_predictable_data() is called from get_mac() in link-config.c and sd_ipv4ll_set_address_seed(), so both of those code paths are affected and will now get data in some cases where they errored out previously. The return code is changed to -ENODATA since that gives a nicer error string. --- src/libsystemd-network/network-internal.c | 19 ++++++++++++------- src/udev/net/link-config.c | 7 +++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index b3b134d6505..9e7838527c5 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "condition.h" #include "conf-parser.h" +#include "device-util.h" #include "dhcp-lease-internal.h" #include "ether-addr-util.h" #include "hexdecoct.h" @@ -40,31 +41,35 @@ const char *net_get_name(sd_device *device) { int net_get_unique_predictable_data(sd_device *device, uint64_t *result) { size_t l, sz = 0; - const char *name = NULL; + const char *name; int r; uint8_t *v; assert(device); + /* net_get_name() will return one of the device names based on stable information about the + * device. If this is not available, we fall back to using the device name. */ name = net_get_name(device); if (!name) - return -ENOENT; + (void) sd_device_get_sysname(device, &name); + if (!name) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA), + "No stable identifying information found"); + log_device_debug(device, "Using \"%s\" as stable identifying information", name); l = strlen(name); sz = sizeof(sd_id128_t) + l; v = alloca(sz); - /* fetch some persistent data unique to this machine */ + /* Fetch some persistent data unique to this machine */ r = sd_id128_get_machine((sd_id128_t*) v); if (r < 0) return r; memcpy(v + sizeof(sd_id128_t), name, l); - /* Let's hash the machine ID plus the device name. We - * use a fixed, but originally randomly created hash - * key here. */ + /* Let's hash the machine ID plus the device name. We use + * a fixed, but originally randomly created hash key here. */ *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); - return 0; } diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index eb2477cea41..0fda4d16b9a 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -313,8 +313,7 @@ static bool mac_is_random(sd_device *device) { return type == NET_ADDR_RANDOM; } -static int get_mac(sd_device *device, bool want_random, - struct ether_addr *mac) { +static int get_mac(sd_device *device, bool want_random, struct ether_addr *mac) { int r; if (want_random) @@ -459,7 +458,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, case MACPOLICY_PERSISTENT: if (mac_is_random(device)) { r = get_mac(device, false, &generated_mac); - if (r == -ENOENT) { + if (r == -ENODATA) { log_warning_errno(r, "Could not generate persistent MAC address for %s: %m", old_name); break; } else if (r < 0) @@ -470,7 +469,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, case MACPOLICY_RANDOM: if (!mac_is_random(device)) { r = get_mac(device, true, &generated_mac); - if (r == -ENOENT) { + if (r == -ENODATA) { log_warning_errno(r, "Could not generate random MAC address for %s: %m", old_name); break; } else if (r < 0) -- 2.39.2