]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
udev,networkd: use the interface name as fallback basis for MAC and IPv4LL seed
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 10 Jan 2019 15:23:49 +0000 (16:23 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 21 Jan 2019 16:33:09 +0000 (17:33 +0100)
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
src/udev/net/link-config.c

index b3b134d65057c467d108c222da695313198b4c63..9e7838527c59fcb85ca0622e2fb06ac6121648f0 100644 (file)
@@ -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;
 }
 
index eb2477cea418b37c07bcda5d7d3b6a079741cf15..0fda4d16b9a02c9b98abe082a5abd0b50222c613 100644 (file)
@@ -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)