From 27dfc98275f7c0d84e1e95a58f50e66d81bfbda2 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 3 Jul 2018 03:19:15 +0900 Subject: [PATCH] network: request product UUID when DUIDType=uuid but DUIDRawData= is not set Closes #9228. --- src/network/networkd-link.c | 139 ++++++++++++++++++++++++++++- src/network/networkd-link.h | 1 + src/network/networkd-manager.c | 59 ++++++++++++ src/network/networkd-manager.h | 7 ++ src/network/networkd-network.c | 3 + src/network/systemd-networkd.pkla | 2 +- src/network/systemd-networkd.rules | 4 +- 7 files changed, 212 insertions(+), 3 deletions(-) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 09bc0ac907..7e4e868d82 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -8,6 +8,7 @@ #include "alloc-util.h" #include "bus-util.h" +#include "dhcp-identifier.h" #include "dhcp-lease-internal.h" #include "fd-util.h" #include "fileio.h" @@ -546,8 +547,10 @@ static void link_free(Link *link) { sd_ndisc_unref(link->ndisc); sd_radv_unref(link->radv); - if (link->manager) + if (link->manager) { hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)); + set_remove(link->manager->links_requesting_uuid, link); + } free(link->ifname); @@ -2891,6 +2894,134 @@ static int link_configure(Link *link) { return link_enter_join_netdev(link); } +static int duid_set_uuid(DUID *duid, sd_id128_t uuid) { + assert(duid); + + if (duid->raw_data_len > 0) + return 0; + + if (duid->type != DUID_TYPE_UUID) + return -EINVAL; + + memcpy(&duid->raw_data, &uuid, sizeof(sd_id128_t)); + duid->raw_data_len = sizeof(sd_id128_t); + + return 1; +} + +int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + Manager *manager = userdata; + const sd_bus_error *e; + const void *a; + size_t sz; + DUID *duid; + Link *link; + int r; + + assert(m); + assert(manager); + + e = sd_bus_message_get_error(m); + if (e) { + log_error_errno(sd_bus_error_get_errno(e), + "Could not get product UUID. Fallback to use application specific machine ID as DUID-UUID: %s", + e->message); + goto configure; + } + + r = sd_bus_message_read_array(m, 'y', &a, &sz); + if (r < 0) + goto configure; + + if (sz != sizeof(sd_id128_t)) { + log_error("Invalid product UUID. Fallback to use application specific machine ID as DUID-UUID."); + goto configure; + } + + memcpy(&manager->product_uuid, a, sz); + while ((duid = set_steal_first(manager->duids_requesting_uuid))) + (void) duid_set_uuid(duid, manager->product_uuid); + + manager->duids_requesting_uuid = set_free(manager->duids_requesting_uuid); + +configure: + while ((link = set_steal_first(manager->links_requesting_uuid))) { + r = link_configure(link); + if (r < 0) + log_link_error_errno(link, r, "Failed to configure link: %m"); + } + + manager->links_requesting_uuid = set_free(manager->links_requesting_uuid); + + /* To avoid calling GetProductUUID() bus method so frequently, set the flag below + * even if the method fails. */ + manager->has_product_uuid = true; + + return 1; +} + +static bool link_requires_uuid(Link *link) { + const DUID *duid; + + assert(link); + assert(link->manager); + assert(link->network); + + duid = link_get_duid(link); + if (duid->type != DUID_TYPE_UUID || duid->raw_data_len != 0) + return false; + + if (link_dhcp4_enabled(link) && IN_SET(link->network->dhcp_client_identifier, DHCP_CLIENT_ID_DUID, DHCP_CLIENT_ID_DUID_ONLY)) + return true; + + if (link_dhcp6_enabled(link) || link_ipv6_accept_ra_enabled(link)) + return true; + + return false; +} + +static int link_configure_duid(Link *link) { + Manager *m; + DUID *duid; + int r; + + assert(link); + assert(link->manager); + assert(link->network); + + m = link->manager; + duid = link_get_duid(link); + + if (!link_requires_uuid(link)) + return 1; + + if (m->has_product_uuid) { + (void) duid_set_uuid(duid, m->product_uuid); + return 1; + } + + if (!m->links_requesting_uuid) { + r = manager_request_product_uuid(m, link); + if (r < 0) { + if (r == -ENOMEM) + return r; + + log_link_warning_errno(link, r, "Failed to get product UUID. Fallback to use application specific machine ID as DUID-UUID: %m"); + return 1; + } + } else { + r = set_put(m->links_requesting_uuid, link); + if (r < 0) + return log_oom(); + + r = set_put(m->duids_requesting_uuid, duid); + if (r < 0) + return log_oom(); + } + + return 0; +} + static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_(link_unrefp) Link *link = userdata; @@ -2946,6 +3077,12 @@ static int link_initialized_and_synced(sd_netlink *rtnl, sd_netlink_message *m, if (r < 0) return r; + /* link_configure_duid() returns 0 if it requests product UUID. In that case, + * link_configure() is called later asynchronously. */ + r = link_configure_duid(link); + if (r <= 0) + return r; + r = link_configure(link); if (r < 0) return r; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 6ed11ff51c..4c13cc156e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -125,6 +125,7 @@ typedef struct Link { } Link; DUID *link_get_duid(Link *link); +int get_product_uuid_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error); Link *link_unref(Link *link); Link *link_ref(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 3792f3d700..4e14fab89f 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -114,6 +114,8 @@ static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *r (void) manager_set_hostname(m, m->dynamic_hostname); if (m->dynamic_timezone) (void) manager_set_timezone(m, m->dynamic_timezone); + if (m->links_requesting_uuid) + (void) manager_request_product_uuid(m, NULL); return 0; } @@ -1459,6 +1461,9 @@ void manager_free(Manager *m) { link_unref(link); hashmap_free(m->links); + set_free(m->links_requesting_uuid); + set_free(m->duids_requesting_uuid); + hashmap_free(m->networks_by_name); while ((netdev = hashmap_first(m->netdevs))) @@ -1831,3 +1836,57 @@ int manager_set_timezone(Manager *m, const char *tz) { return 0; } + +int manager_request_product_uuid(Manager *m, Link *link) { + int r; + + assert(m); + + if (m->has_product_uuid) + return 0; + + log_debug("Requesting product UUID"); + + if (link) { + DUID *duid; + + assert_se(duid = link_get_duid(link)); + + r = set_ensure_allocated(&m->links_requesting_uuid, NULL); + if (r < 0) + return log_oom(); + + r = set_ensure_allocated(&m->duids_requesting_uuid, NULL); + if (r < 0) + return log_oom(); + + r = set_put(m->links_requesting_uuid, link); + if (r < 0) + return log_oom(); + + r = set_put(m->duids_requesting_uuid, duid); + if (r < 0) + return log_oom(); + } + + if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { + log_debug("Not connected to system bus, requesting product UUID later."); + return 0; + } + + r = sd_bus_call_method_async( + m->bus, + NULL, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + "org.freedesktop.hostname1", + "GetProductUUID", + get_product_uuid_handler, + m, + "b", + false); + if (r < 0) + return log_warning_errno(r, "Failed to get product UUID: %m"); + + return 0; +} diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index d6e3fe5478..410e00da63 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -5,6 +5,7 @@ #include "sd-bus.h" #include "sd-event.h" +#include "sd-id128.h" #include "sd-netlink.h" #include "sd-resolve.h" #include "udev.h" @@ -48,6 +49,11 @@ struct Manager { usec_t network_dirs_ts_usec; DUID duid; + sd_id128_t product_uuid; + bool has_product_uuid; + Set *links_requesting_uuid; + Set *duids_requesting_uuid; + char* dynamic_hostname; char* dynamic_timezone; @@ -85,6 +91,7 @@ Link* manager_find_uplink(Manager *m, Link *exclude); int manager_set_hostname(Manager *m, const char *hostname); int manager_set_timezone(Manager *m, const char *timezone); +int manager_request_product_uuid(Manager *m, Link *link); Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr); int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link); diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 77ba8c678e..2fa067655e 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -415,6 +415,9 @@ void network_free(Network *network) { if (network->manager->networks_by_name) hashmap_remove(network->manager->networks_by_name, network->name); + + if (network->manager->duids_requesting_uuid) + set_remove(network->manager->duids_requesting_uuid, &network->duid); } free(network->name); diff --git a/src/network/systemd-networkd.pkla b/src/network/systemd-networkd.pkla index fb257d933b..4d1bb4585e 100644 --- a/src/network/systemd-networkd.pkla +++ b/src/network/systemd-networkd.pkla @@ -1,4 +1,4 @@ [Allow systemd-networkd to set timezone and transient hostname] Identity=unix-user:systemd-network -Action=org.freedesktop.hostname1.set-hostname;org.freedesktop.timedate1.set-timezone; +Action=org.freedesktop.hostname1.set-hostname;org.freedesktop.hostname1.get-product-uuid;org.freedesktop.timedate1.set-timezone; ResultAny=yes diff --git a/src/network/systemd-networkd.rules b/src/network/systemd-networkd.rules index 2e4bc42bfb..b9077c1ea2 100644 --- a/src/network/systemd-networkd.rules +++ b/src/network/systemd-networkd.rules @@ -1,6 +1,8 @@ -// Allow systemd-networkd to set timezone and transient hostname +// Allow systemd-networkd to set timezone, get product UUID, +// and transient hostname polkit.addRule(function(action, subject) { if ((action.id == "org.freedesktop.hostname1.set-hostname" || + action.id == "org.freedesktop.hostname1.get-product-uuid" || action.id == "org.freedesktop.timedate1.set-timezone") && subject.user == "systemd-network") { return polkit.Result.YES;