diff options
author | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2018-08-23 11:11:13 +0200 |
---|---|---|
committer | Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> | 2018-08-23 11:11:13 +0200 |
commit | c07fe6d0df3e433b579c9cb8b8ea9cfb2fb62173 (patch) | |
tree | 4759c1c46cc17eb8b175224ad967ad83cc50b06b /src | |
parent | Networkd: Start DHCP server when link is up. (diff) | |
parent | man: mention that DUIDType=link-layer-time or link-layer uses MAC address (diff) | |
download | systemd-c07fe6d0df3e433b579c9cb8b8ea9cfb2fb62173.tar.xz systemd-c07fe6d0df3e433b579c9cb8b8ea9cfb2fb62173.zip |
Merge pull request #9406 from yuwata/rfe-9228
Trivial conflict solved in merge and include net/if_arp.h added.
Diffstat (limited to 'src')
24 files changed, 637 insertions, 130 deletions
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index b98f962ad2..d8074e6f5f 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -6,11 +6,13 @@ #include <unistd.h> #include "alloc-util.h" +#include "bus-common-errors.h" #include "bus-util.h" #include "def.h" #include "env-util.h" #include "fileio-label.h" #include "hostname-util.h" +#include "id128-util.h" #include "os-util.h" #include "parse-util.h" #include "path-util.h" @@ -43,6 +45,8 @@ enum { typedef struct Context { char *data[_PROP_MAX]; Hashmap *polkit_registry; + sd_id128_t uuid; + bool has_uuid; } Context; static void context_reset(Context *c) { @@ -103,6 +107,11 @@ static int context_read_data(Context *c) { if (r < 0 && r != -ENOENT) return r; + r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &c->uuid); + if (r < 0) + log_info_errno(r, "Failed to read product UUID, ignoring: %m"); + c->has_uuid = (r >= 0); + return 0; } @@ -598,6 +607,46 @@ static int method_set_location(sd_bus_message *m, void *userdata, sd_bus_error * return set_machine_info(userdata, m, PROP_LOCATION, method_set_location, error); } +static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Context *c = userdata; + int interactive, r; + + assert(m); + assert(c); + + if (!c->has_uuid) + return sd_bus_error_set(error, BUS_ERROR_NO_PRODUCT_UUID, "Failed to read product UUID from /sys/class/dmi/id/product_uuid"); + + r = sd_bus_message_read(m, "b", &interactive); + if (r < 0) + return r; + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.hostname1.get-product-uuid", + NULL, + interactive, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &c->uuid, sizeof(c->uuid)); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + static const sd_bus_vtable hostname_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Hostname", "s", NULL, offsetof(Context, data) + sizeof(char*) * PROP_HOSTNAME, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -620,6 +669,7 @@ static const sd_bus_vtable hostname_vtable[] = { SD_BUS_METHOD("SetChassis", "sb", NULL, method_set_chassis, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetDeployment", "sb", NULL, method_set_deployment, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLocation", "sb", NULL, method_set_location, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetProductUUID", "b", "ay", method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END, }; diff --git a/src/hostname/org.freedesktop.hostname1.policy b/src/hostname/org.freedesktop.hostname1.policy index e1519ce86b..5bedc0b694 100644 --- a/src/hostname/org.freedesktop.hostname1.policy +++ b/src/hostname/org.freedesktop.hostname1.policy @@ -47,4 +47,14 @@ </defaults> </action> + <action id="org.freedesktop.hostname1.get-product-uuid"> + <description gettext-domain="systemd">Get product UUID</description> + <message gettext-domain="systemd">Authentication is required to get product UUID.</message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + </policyconfig> diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index d1e929ba7b..531cf6783b 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include <linux/if_infiniband.h> +#include <net/if_arp.h> + #include "sd-device.h" #include "sd-id128.h" @@ -10,8 +13,10 @@ #include "sparse-endian.h" #include "virt.h" -#define SYSTEMD_PEN 43793 -#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) +#define SYSTEMD_PEN 43793 +#define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) +#define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) +#define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) { struct duid d; @@ -44,6 +49,56 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) { return 0; } +int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { + uint16_t time_from_2000y; + + assert(duid); + assert(len); + assert(addr); + + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + if (t < USEC_2000) + time_from_2000y = 0; + else + time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); + + unaligned_write_be16(&duid->type, DUID_TYPE_LLT); + unaligned_write_be16(&duid->llt.htype, arp_type); + unaligned_write_be32(&duid->llt.time, time_from_2000y); + memcpy(duid->llt.haddr, addr, addr_len); + + *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len; + + return 0; +} + +int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { + assert(duid); + assert(len); + assert(addr); + + if (arp_type == ARPHRD_ETHER) + assert_return(addr_len == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); + else + return -EINVAL; + + unaligned_write_be16(&duid->type, DUID_TYPE_LL); + unaligned_write_be16(&duid->ll.htype, arp_type); + memcpy(duid->ll.haddr, addr, addr_len); + + *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len; + + return 0; +} + int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { sd_id128_t machine_id; uint64_t hash; @@ -62,13 +117,32 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { *len = sizeof(duid->type) + sizeof(duid->en); /* a bit of snake-oil perhaps, but no need to expose the machine-id - directly; duid->en.id might not be aligned, so we need to copy */ + * directly; duid->en.id might not be aligned, so we need to copy */ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); memcpy(duid->en.id, &hash, sizeof(duid->en.id)); return 0; } +int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { + sd_id128_t machine_id; + int r; + + assert(duid); + assert(len); + + r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); + if (r < 0) + return r; + + unaligned_write_be16(&duid->type, DUID_TYPE_UUID); + memcpy(&duid->raw.data, &machine_id, sizeof(machine_id)); + + *len = sizeof(duid->type) + sizeof(machine_id); + + return 0; +} + int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) { /* name is a pointer to memory in the sd_device struct, so must * have the same scope */ diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index 8250c76598..64315d3a3b 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -5,6 +5,7 @@ #include "macro.h" #include "sparse-endian.h" +#include "time-util.h" #include "unaligned.h" typedef enum DUIDType { @@ -27,18 +28,18 @@ struct duid { union { struct { /* DUID_TYPE_LLT */ - uint16_t htype; - uint32_t time; + be16_t htype; + be32_t time; uint8_t haddr[0]; } _packed_ llt; struct { /* DUID_TYPE_EN */ - uint32_t pen; + be32_t pen; uint8_t id[8]; } _packed_ en; struct { /* DUID_TYPE_LL */ - int16_t htype; + be16_t htype; uint8_t haddr[0]; } _packed_ ll; struct { @@ -52,5 +53,8 @@ struct duid { } _packed_; int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len); +int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); +int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); +int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index ff434f8ce7..de86028a5d 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -341,13 +341,14 @@ int sd_dhcp_client_set_client_id( * without further modification. Otherwise, if duid_type is supported, DUID * is set based on that type. Otherwise, an error is returned. */ -static int dhcp_client_set_iaid_duid( +static int dhcp_client_set_iaid_duid_internal( sd_dhcp_client *client, uint32_t iaid, bool append_iaid, uint16_t duid_type, const void *duid, - size_t duid_len) { + size_t duid_len, + usec_t llt_time) { DHCP_CLIENT_DONT_DESTROY(client); int r; @@ -381,18 +382,43 @@ static int dhcp_client_set_iaid_duid( client->client_id.ns.duid.type = htobe16(duid_type); memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); len = sizeof(client->client_id.ns.duid.type) + duid_len; - } else if (duid_type == DUID_TYPE_EN) { - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); - if (r < 0) - return r; } else - return -EOPNOTSUPP; + switch (duid_type) { + case DUID_TYPE_LLT: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); + if (r < 0) + return r; + break; + case DUID_TYPE_EN: + r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); + if (r < 0) + return r; + break; + case DUID_TYPE_LL: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); + if (r < 0) + return r; + break; + case DUID_TYPE_UUID: + r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); + if (r < 0) + return r; + break; + default: + return -EINVAL; + } client->client_id_len = sizeof(client->client_id.type) + len + (append_iaid ? sizeof(client->client_id.ns.iaid) : 0); if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { - log_dhcp_client(client, "Configured IAID+DUID, restarting."); + log_dhcp_client(client, "Configured %sDUID, restarting.", append_iaid ? "IAID+" : ""); client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); sd_dhcp_client_start(client); } @@ -406,7 +432,14 @@ int sd_dhcp_client_set_iaid_duid( uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid(client, iaid, true, duid_type, duid, duid_len); + return dhcp_client_set_iaid_duid_internal(client, iaid, true, duid_type, duid, duid_len, 0); +} + +int sd_dhcp_client_set_iaid_duid_llt( + sd_dhcp_client *client, + uint32_t iaid, + usec_t llt_time) { + return dhcp_client_set_iaid_duid_internal(client, iaid, true, DUID_TYPE_LLT, NULL, 0, llt_time); } int sd_dhcp_client_set_duid( @@ -414,7 +447,13 @@ int sd_dhcp_client_set_duid( uint16_t duid_type, const void *duid, size_t duid_len) { - return dhcp_client_set_iaid_duid(client, 0, false, duid_type, duid, duid_len); + return dhcp_client_set_iaid_duid_internal(client, 0, false, duid_type, duid, duid_len, 0); +} + +int sd_dhcp_client_set_duid_llt( + sd_dhcp_client *client, + usec_t llt_time) { + return dhcp_client_set_iaid_duid_internal(client, 0, false, DUID_TYPE_LLT, NULL, 0, llt_time); } int sd_dhcp_client_set_hostname( diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index ff54d7e20c..84c58af346 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -182,13 +182,14 @@ static int client_ensure_duid(sd_dhcp6_client *client) { * without further modification. Otherwise, if duid_type is supported, DUID * is set based on that type. Otherwise, an error is returned. */ -int sd_dhcp6_client_set_duid( +static int dhcp6_client_set_duid_internal( sd_dhcp6_client *client, uint16_t duid_type, const void *duid, - size_t duid_len) { - + size_t duid_len, + usec_t llt_time) { int r; + assert_return(client, -EINVAL); assert_return(duid_len == 0 || duid != NULL, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); @@ -197,22 +198,59 @@ int sd_dhcp6_client_set_duid( r = dhcp_validate_duid_len(duid_type, duid_len); if (r < 0) return r; - } - if (duid != NULL) { client->duid.type = htobe16(duid_type); memcpy(&client->duid.raw.data, duid, duid_len); client->duid_len = sizeof(client->duid.type) + duid_len; - } else if (duid_type == DUID_TYPE_EN) { - r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); - if (r < 0) - return r; } else - return -EOPNOTSUPP; + switch (duid_type) { + case DUID_TYPE_LLT: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_llt(&client->duid, 0, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + if (r < 0) + return r; + break; + case DUID_TYPE_EN: + r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + if (r < 0) + return r; + break; + case DUID_TYPE_LL: + if (!client->mac_addr || client->mac_addr_len == 0) + return -EOPNOTSUPP; + + r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); + if (r < 0) + return r; + break; + case DUID_TYPE_UUID: + r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len); + if (r < 0) + return r; + break; + default: + return -EINVAL; + } return 0; } +int sd_dhcp6_client_set_duid( + sd_dhcp6_client *client, + uint16_t duid_type, + const void *duid, + size_t duid_len) { + return dhcp6_client_set_duid_internal(client, duid_type, duid, duid_len, 0); +} + +int sd_dhcp6_client_set_duid_llt( + sd_dhcp6_client *client, + usec_t llt_time) { + return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time); +} + int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index ff0790bf5a..b6e40ee8a9 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -94,5 +94,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_TRANSFER, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSFER_IN_PROGRESS, EBUSY), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRODUCT_UUID, EOPNOTSUPP), + SD_BUS_ERROR_MAP_END }; diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index daf7cdae67..a76a93644c 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -75,4 +75,6 @@ #define BUS_ERROR_NO_SUCH_TRANSFER "org.freedesktop.import1.NoSuchTransfer" #define BUS_ERROR_TRANSFER_IN_PROGRESS "org.freedesktop.import1.TransferInProgress" +#define BUS_ERROR_NO_PRODUCT_UUID "org.freedesktop.hostname1.NoProductUUID" + BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c index 05bbc0b3e9..334ffc3c57 100644 --- a/src/network/networkd-conf.c +++ b/src/network/networkd-conf.c @@ -31,7 +31,74 @@ static const char* const duid_type_table[_DUID_TYPE_MAX] = { [DUID_TYPE_UUID] = "uuid", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType); -DEFINE_CONFIG_PARSE_ENUM(config_parse_duid_type, duid_type, DUIDType, "Failed to parse DUID type"); + +int config_parse_duid_type( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ char *type_string = NULL; + const char *p = rvalue; + DUID *duid = data; + DUIDType type; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(duid); + + r = extract_first_word(&p, &type_string, ":", 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + if (r == 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to extract DUID type from '%s', ignoring.", rvalue); + return 0; + } + + type = duid_type_from_string(type_string); + if (type < 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse DUID type '%s', ignoring.", type_string); + return 0; + } + + if (!isempty(p)) { + usec_t u; + + if (type != DUID_TYPE_LLT) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + + r = parse_timestamp(p, &u); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse timestamp, ignoring: %s", p); + return 0; + } + + duid->llt_time = u; + } + + duid->type = type; + + return 0; +} int config_parse_duid_rawdata( const char *unit, diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 7798844e03..18867d9401 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -660,6 +660,63 @@ int dhcp4_set_promote_secondaries(Link *link) { return 0; } +int dhcp4_set_client_identifier(Link *link) { + int r; + + assert(link); + assert(link->network); + assert(link->dhcp_client); + + switch (link->network->dhcp_client_identifier) { + case DHCP_CLIENT_ID_DUID: { + /* If configured, apply user specified DUID and IAID */ + const DUID *duid = link_get_duid(link); + + if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0) + r = sd_dhcp_client_set_iaid_duid_llt(link->dhcp_client, + link->network->iaid, + duid->llt_time); + else + r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set IAID+DUID: %m"); + break; + } + case DHCP_CLIENT_ID_DUID_ONLY: { + /* If configured, apply user specified DUID */ + const DUID *duid = link_get_duid(link); + + if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0) + r = sd_dhcp_client_set_duid_llt(link->dhcp_client, + duid->llt_time); + else + r = sd_dhcp_client_set_duid(link->dhcp_client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m"); + break; + } + case DHCP_CLIENT_ID_MAC: + r = sd_dhcp_client_set_client_id(link->dhcp_client, + ARPHRD_ETHER, + (const uint8_t *) &link->mac, + sizeof(link->mac)); + if (r < 0) + return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m"); + break; + default: + assert_not_reached("Unknown client identifier type."); + } + + return 0; +} + int dhcp4_configure(Link *link) { int r; @@ -763,43 +820,5 @@ int dhcp4_configure(Link *link) { return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set listen port: %m"); } - switch (link->network->dhcp_client_identifier) { - case DHCP_CLIENT_ID_DUID: { - /* If configured, apply user specified DUID and/or IAID */ - const DUID *duid = link_duid(link); - - r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, - link->network->iaid, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m"); - break; - } - case DHCP_CLIENT_ID_DUID_ONLY: { - /* If configured, apply user specified DUID */ - const DUID *duid = link_duid(link); - - r = sd_dhcp_client_set_duid(link->dhcp_client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set DUID: %m"); - break; - } - case DHCP_CLIENT_ID_MAC: - r = sd_dhcp_client_set_client_id(link->dhcp_client, - ARPHRD_ETHER, - (const uint8_t *) &link->mac, - sizeof(link->mac)); - if (r < 0) - return log_link_error_errno(link, r, "DHCP4 CLIENT: Failed to set client ID: %m"); - break; - default: - assert_not_reached("Unknown client identifier type."); - } - - return 0; + return dhcp4_set_client_identifier(link); } diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 0ec4deb716..fa6e18cd2c 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -494,11 +494,14 @@ int dhcp6_configure(Link *link) { if (r < 0) return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set IAID: %m"); - duid = link_duid(link); - r = sd_dhcp6_client_set_duid(client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); + duid = link_get_duid(link); + if (duid->type == DUID_TYPE_LLT && duid->raw_data_len == 0) + r = sd_dhcp6_client_set_duid_llt(client, duid->llt_time); + else + r = sd_dhcp6_client_set_duid(client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); if (r < 0) return log_link_error_errno(link, r, "DHCP6 CLIENT: Failed to set DUID: %m"); diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf index 54161446bb..cc88fc0add 100644 --- a/src/network/networkd-gperf.gperf +++ b/src/network/networkd-gperf.gperf @@ -18,5 +18,5 @@ struct ConfigPerfItem; %struct-type %includes %% -DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid.type) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid) diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 96e1119567..ab8126781d 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" @@ -27,6 +28,13 @@ #include "util.h" #include "virt.h" +DUID* link_get_duid(Link *link) { + if (link->network->duid.type != _DUID_TYPE_INVALID) + return &link->network->duid; + else + return &link->manager->duid; +} + static bool link_dhcp6_enabled(Link *link) { assert(link); @@ -539,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); @@ -2884,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; @@ -2939,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; @@ -3433,45 +3577,13 @@ int link_update(Link *link, sd_netlink_message *m) { if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); - switch (link->network->dhcp_client_identifier) { - case DHCP_CLIENT_ID_DUID: { - const DUID *duid = link_duid(link); - - r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, - link->network->iaid, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m"); - break; - } - case DHCP_CLIENT_ID_DUID_ONLY: { - const DUID *duid = link_duid(link); - - r = sd_dhcp_client_set_duid(link->dhcp_client, - duid->type, - duid->raw_data_len > 0 ? duid->raw_data : NULL, - duid->raw_data_len); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update DUID in DHCP client: %m"); - break; - } - case DHCP_CLIENT_ID_MAC: - r = sd_dhcp_client_set_client_id(link->dhcp_client, - ARPHRD_ETHER, - (const uint8_t *)&link->mac, - sizeof(link->mac)); - if (r < 0) - return log_link_warning_errno(link, r, "Could not update MAC client id in DHCP client: %m"); - break; - default: - assert_not_reached("Unknown client identifier type."); - } + r = dhcp4_set_client_identifier(link); + if (r < 0) + return r; } if (link->dhcp6_client) { - const DUID* duid = link_duid(link); + const DUID* duid = link_get_duid(link); r = sd_dhcp6_client_set_mac(link->dhcp6_client, (const uint8_t *) &link->mac, diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index b6f8ce1385..912952e651 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -44,6 +44,7 @@ typedef enum LinkOperationalState { typedef struct Manager Manager; typedef struct Network Network; typedef struct Address Address; +typedef struct DUID DUID; typedef struct Link { Manager *manager; @@ -124,6 +125,9 @@ typedef struct Link { Hashmap *bound_to_links; } 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); int link_get(Manager *m, int ifindex, Link **ret); @@ -157,6 +161,7 @@ int link_set_mtu(Link *link, uint32_t mtu); int ipv4ll_configure(Link *link); int dhcp4_configure(Link *link); +int dhcp4_set_client_identifier(Link *link); int dhcp4_set_promote_secondaries(Link *link); int dhcp6_configure(Link *link); int dhcp6_request_address(Link *link, int ir); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 7462a62215..27a4f6db61 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -113,6 +113,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; } @@ -1457,6 +1459,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))) @@ -1828,3 +1833,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 edaafb6a06..fbfcdad304 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 "libudev.h" @@ -47,6 +48,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; @@ -55,13 +61,6 @@ struct Manager { Set *rules_saved; }; -static inline const DUID* link_duid(const Link *link) { - if (link->network->duid.type != _DUID_TYPE_INVALID) - return &link->network->duid; - else - return &link->manager->duid; -} - extern const sd_bus_vtable manager_vtable[]; int manager_new(Manager **ret); @@ -91,6 +90,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-gperf.gperf b/src/network/networkd-network-gperf.gperf index 6ad5257f79..357231152e 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -130,7 +130,7 @@ DHCP.RequestBroadcast, config_parse_bool, DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class) -DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) DHCP.RouteTable, config_parse_dhcp_route_table, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 10c5cb4163..c7d162de67 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/networkd-network.h b/src/network/networkd-network.h index efb9efc560..6bfd8d5dd5 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -70,6 +70,7 @@ typedef struct DUID { uint8_t raw_data_len; uint8_t raw_data[MAX_DUID_LEN]; + usec_t llt_time; } DUID; typedef enum RADVPrefixDelegation { 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; diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index fc1646f4bf..d05680d7e1 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -11,23 +11,30 @@ #include "networkd-conf.h" #include "networkd-network.h" -static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected) { - DUIDType actual = 0; +static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected, usec_t expected_time) { + DUID actual = {}; int r; r = config_parse_duid_type("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); - log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual); + log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual.type); assert_se(r == ret); - assert_se(expected == actual); + assert_se(expected == actual.type); + if (expected == DUID_TYPE_LLT) + assert_se(expected_time == actual.llt_time); } static void test_config_parse_duid_type(void) { - test_config_parse_duid_type_one("", 0, 0); - test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT); - test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN); - test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL); - test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID); - test_config_parse_duid_type_one("foo", 0, 0); + test_config_parse_duid_type_one("", 0, 0, 0); + test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT, 0); + test_config_parse_duid_type_one("link-layer-time:2000-01-01 00:00:00 UTC", 0, DUID_TYPE_LLT, (usec_t) 946684800000000); + test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN, 0); + test_config_parse_duid_type_one("vendor:2000-01-01 00:00:00 UTC", 0, 0, 0); + test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL, 0); + test_config_parse_duid_type_one("link-layer:2000-01-01 00:00:00 UTC", 0, 0, 0); + test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID, 0); + test_config_parse_duid_type_one("uuid:2000-01-01 00:00:00 UTC", 0, 0, 0); + test_config_parse_duid_type_one("foo", 0, 0, 0); + test_config_parse_duid_type_one("foo:2000-01-01 00:00:00 UTC", 0, 0, 0); } static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, const DUID* expected) { diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 931b0e890b..e388552064 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -131,11 +131,18 @@ int sd_dhcp_client_set_iaid_duid( uint16_t duid_type, const void *duid, size_t duid_len); +int sd_dhcp_client_set_iaid_duid_llt( + sd_dhcp_client *client, + uint32_t iaid, + uint64_t llt_time); int sd_dhcp_client_set_duid( sd_dhcp_client *client, uint16_t duid_type, const void *duid, size_t duid_len); +int sd_dhcp_client_set_duid_llt( + sd_dhcp_client *client, + uint64_t llt_time); int sd_dhcp_client_get_client_id( sd_dhcp_client *client, uint8_t *type, diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 4f3b2d9e2e..fa36dca909 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -102,6 +102,9 @@ int sd_dhcp6_client_set_duid( uint16_t duid_type, const void *duid, size_t duid_len); +int sd_dhcp6_client_set_duid_llt( + sd_dhcp6_client *client, + uint64_t llt_time); int sd_dhcp6_client_set_iaid( sd_dhcp6_client *client, uint32_t iaid); |