diff options
-rw-r--r-- | src/libsystemd-network/dhcp-duid-internal.h (renamed from src/libsystemd-network/dhcp-identifier.h) | 58 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-identifier.c | 209 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-internal.h | 5 | ||||
-rw-r--r-- | src/libsystemd-network/meson.build | 2 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-client.c | 88 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-duid.c | 241 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 28 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-lease.c | 2 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp-client.c | 11 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp6-client.c | 2 | ||||
-rw-r--r-- | src/network/networkd-dhcp-common.c | 1 | ||||
-rw-r--r-- | src/network/networkd-dhcp-common.h | 2 | ||||
-rw-r--r-- | src/network/networkd-link.c | 1 | ||||
-rw-r--r-- | src/network/networkd-manager.h | 2 | ||||
-rw-r--r-- | src/systemd/meson.build | 1 | ||||
-rw-r--r-- | src/systemd/sd-dhcp-duid.h | 69 |
16 files changed, 404 insertions, 318 deletions
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-duid-internal.h index cc1a2008a0..ae888f789a 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-duid-internal.h @@ -2,30 +2,31 @@ #pragma once #include "sd-device.h" +#include "sd-dhcp-duid.h" #include "sd-id128.h" #include "ether-addr-util.h" #include "macro.h" #include "sparse-endian.h" -#include "time-util.h" #define SYSTEMD_PEN 43793 typedef enum DUIDType { - DUID_TYPE_LLT = 1, - DUID_TYPE_EN = 2, - DUID_TYPE_LL = 3, - DUID_TYPE_UUID = 4, + DUID_TYPE_LLT = SD_DUID_TYPE_LLT, + DUID_TYPE_EN = SD_DUID_TYPE_EN, + DUID_TYPE_LL = SD_DUID_TYPE_LL, + DUID_TYPE_UUID = SD_DUID_TYPE_UUID, _DUID_TYPE_MAX, - _DUID_TYPE_INVALID = -EINVAL, - _DUID_TYPE_FORCE_U16 = UINT16_MAX, + _DUID_TYPE_INVALID = -EINVAL, } DUIDType; /* RFC 8415 section 11.1: * A DUID consists of a 2-octet type code represented in network byte order, followed by a variable number of * octets that make up the actual identifier. The length of the DUID (not including the type code) is at * least 1 octet and at most 128 octets. */ +#define MIN_DUID_DATA_LEN 1 #define MAX_DUID_DATA_LEN 128 +#define MIN_DUID_LEN (sizeof(be16_t) + MIN_DUID_DATA_LEN) #define MAX_DUID_LEN (sizeof(be16_t) + MAX_DUID_DATA_LEN) /* https://tools.ietf.org/html/rfc3315#section-9.1 */ @@ -52,35 +53,30 @@ struct duid { /* DUID_TYPE_UUID */ sd_id128_t uuid; } _packed_ uuid; - struct { - uint8_t data[MAX_DUID_DATA_LEN]; - } _packed_ raw; + uint8_t data[MAX_DUID_DATA_LEN]; }; } _packed_; -int dhcp_identifier_set_duid_llt( - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - usec_t t, - struct duid *ret_duid, - size_t *ret_len); -int dhcp_identifier_set_duid_ll( - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - struct duid *ret_duid, - size_t *ret_len); -int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len); -int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len); -int dhcp_identifier_set_duid_raw( - DUIDType duid_type, - const uint8_t *buf, - size_t buf_len, - struct duid *ret_duid, - size_t *ret_len); +typedef struct sd_dhcp_duid { + size_t size; + union { + struct duid duid; + uint8_t raw[MAX_DUID_LEN]; + }; +} sd_dhcp_duid; + +static inline bool duid_size_is_valid(size_t size) { + return size >= MIN_DUID_LEN && size <= MAX_DUID_LEN; +} + +static inline bool duid_data_size_is_valid(size_t size) { + return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN; +} + +const char *duid_type_to_string(DUIDType t) _const_; + int dhcp_identifier_set_iaid( sd_device *dev, const struct hw_addr_data *hw_addr, bool legacy_unstable_byteorder, void *ret); - -const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c deleted file mode 100644 index 09bab592dc..0000000000 --- a/src/libsystemd-network/dhcp-identifier.c +++ /dev/null @@ -1,209 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ - -#include <linux/if_infiniband.h> -#include <net/ethernet.h> -#include <net/if_arp.h> - -#include "dhcp-identifier.h" -#include "netif-util.h" -#include "network-common.h" -#include "siphash24.h" -#include "string-table.h" -#include "unaligned.h" - -#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 */ - -static const char * const duid_type_table[_DUID_TYPE_MAX] = { - [DUID_TYPE_LLT] = "DUID-LLT", - [DUID_TYPE_EN] = "DUID-EN/Vendor", - [DUID_TYPE_LL] = "DUID-LL", - [DUID_TYPE_UUID] = "UUID", -}; - -DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); - -int dhcp_identifier_set_duid_llt( - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - usec_t t, - struct duid *ret_duid, - size_t *ret_len) { - - uint16_t time_from_2000y; - - assert(hw_addr); - assert(ret_duid); - assert(ret_len); - - if (hw_addr->length == 0) - return -EOPNOTSUPP; - - if (arp_type == ARPHRD_ETHER) - assert_return(hw_addr->length == ETH_ALEN, -EINVAL); - else if (arp_type == ARPHRD_INFINIBAND) - assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL); - else - return -EOPNOTSUPP; - - if (t < USEC_2000) - time_from_2000y = 0; - else - time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); - - unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT); - unaligned_write_be16(&ret_duid->llt.htype, arp_type); - unaligned_write_be32(&ret_duid->llt.time, time_from_2000y); - memcpy(ret_duid->llt.haddr, hw_addr->bytes, hw_addr->length); - - *ret_len = offsetof(struct duid, llt.haddr) + hw_addr->length; - - return 0; -} - -int dhcp_identifier_set_duid_ll( - const struct hw_addr_data *hw_addr, - uint16_t arp_type, - struct duid *ret_duid, - size_t *ret_len) { - - assert(hw_addr); - assert(ret_duid); - assert(ret_len); - - if (hw_addr->length == 0) - return -EOPNOTSUPP; - - if (arp_type == ARPHRD_ETHER) - assert_return(hw_addr->length == ETH_ALEN, -EINVAL); - else if (arp_type == ARPHRD_INFINIBAND) - assert_return(hw_addr->length == INFINIBAND_ALEN, -EINVAL); - else - return -EOPNOTSUPP; - - unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL); - unaligned_write_be16(&ret_duid->ll.htype, arp_type); - memcpy(ret_duid->ll.haddr, hw_addr->bytes, hw_addr->length); - - *ret_len = offsetof(struct duid, ll.haddr) + hw_addr->length; - - return 0; -} - -int dhcp_identifier_set_duid_en(struct duid *ret_duid, size_t *ret_len) { - sd_id128_t machine_id; - bool test_mode; - uint64_t hash; - int r; - - assert(ret_duid); - assert(ret_len); - - test_mode = network_test_mode_enabled(); - - if (!test_mode) { - r = sd_id128_get_machine(&machine_id); - if (r < 0) - return r; - } else - /* For tests, especially for fuzzers, reproducibility is important. - * Hence, use a static and constant machine ID. - * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */ - machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); - - unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN); - unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN); - - /* 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 */ - hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); - memcpy(ret_duid->en.id, &hash, sizeof(hash)); - - *ret_len = offsetof(struct duid, en.id) + sizeof(hash); - - if (test_mode) - assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0); - - return 0; -} - -int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) { - sd_id128_t machine_id; - int r; - - assert(ret_duid); - assert(ret_len); - - r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); - if (r < 0) - return r; - - unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID); - memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id)); - - *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id); - - return 0; -} - -int dhcp_identifier_set_duid_raw( - DUIDType duid_type, - const uint8_t *buf, - size_t buf_len, - struct duid *ret_duid, - size_t *ret_len) { - - assert(buf || buf_len == 0); - assert(ret_duid); - assert(ret_len); - - if (duid_type < 0 || duid_type > UINT16_MAX) - return -EINVAL; - - if (buf_len > MAX_DUID_DATA_LEN) - return -EINVAL; - - unaligned_write_be16(&ret_duid->type, duid_type); - memcpy_safe(ret_duid->raw.data, buf, buf_len); - - *ret_len = offsetof(struct duid, raw.data) + buf_len; - return 0; -} - -int dhcp_identifier_set_iaid( - sd_device *dev, - const struct hw_addr_data *hw_addr, - bool legacy_unstable_byteorder, - void *ret) { - - const char *name = NULL; - uint32_t id32; - uint64_t id; - - assert(hw_addr); - assert(ret); - - if (dev) - name = net_get_persistent_name(dev); - if (name) - id = siphash24(name, strlen(name), HASH_KEY.bytes); - else - /* fall back to MAC address if no predictable name available */ - id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes); - - id32 = (id & 0xffffffff) ^ (id >> 32); - - if (legacy_unstable_byteorder) - /* for historical reasons (a bug), the bits were swapped and thus - * the result was endianness dependent. Preserve that behavior. */ - id32 = bswap_32(id32); - else - /* the fixed behavior returns a stable byte order. Since LE is expected - * to be more common, swap the bytes on LE to give the same as legacy - * behavior. */ - id32 = be32toh(id32); - - unaligned_write_ne32(ret, id32); - return 0; -} diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index e5b3b1302c..3fbfc028e9 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -11,7 +11,7 @@ #include "sd-event.h" #include "sd-dhcp6-client.h" -#include "dhcp-identifier.h" +#include "dhcp-duid-internal.h" #include "dhcp6-client-internal.h" #include "dhcp6-option.h" #include "dhcp6-protocol.h" @@ -64,8 +64,7 @@ struct sd_dhcp6_client { DHCP6IA ia_na; DHCP6IA ia_pd; DHCP6RequestIA request_ia; - struct duid duid; - size_t duid_len; + sd_dhcp_duid duid; be16_t *req_opts; size_t n_req_opts; char *fqdn; diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index 93186e23a1..510b0ed99e 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -2,7 +2,6 @@ sources = files( 'arp-util.c', - 'dhcp-identifier.c', 'dhcp-network.c', 'dhcp-option.c', 'dhcp-packet.c', @@ -17,6 +16,7 @@ sources = files( 'network-common.c', 'network-internal.c', 'sd-dhcp-client.c', + 'sd-dhcp-duid.c', 'sd-dhcp-lease.c', 'sd-dhcp-server.c', 'sd-dhcp6-client.c', diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 67911a2b93..8e370f1d7e 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -16,7 +16,7 @@ #include "alloc-util.h" #include "device-util.h" #include "dhcp-client-internal.h" -#include "dhcp-identifier.h" +#include "dhcp-duid-internal.h" #include "dhcp-lease-internal.h" #include "dhcp-network.h" #include "dhcp-option.h" @@ -451,29 +451,45 @@ static int dhcp_client_set_iaid( return 0; } -int sd_dhcp_client_set_iaid_duid_llt( +static int dhcp_client_set_iaid_duid( sd_dhcp_client *client, bool iaid_set, uint32_t iaid, - usec_t llt_time) { + sd_dhcp_duid *duid) { - size_t len; int r; assert_return(client, -EINVAL); assert_return(!sd_dhcp_client_is_running(client), -EBUSY); + assert_return(duid, -EINVAL); + assert_return(sd_dhcp_duid_is_set(duid), -ESTALE); r = dhcp_client_set_iaid(client, iaid_set, iaid); if (r < 0) return r; - r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m"); + memcpy(&client->client_id.ns.duid, &duid->duid, duid->size); + client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + duid->size; + return 0; +} + +int sd_dhcp_client_set_iaid_duid_llt( + sd_dhcp_client *client, + bool iaid_set, + uint32_t iaid, + usec_t llt_time) { - client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len; + sd_dhcp_duid duid; + int r; - return 0; + assert_return(client, -EINVAL); + assert_return(!sd_dhcp_client_is_running(client), -EBUSY); + + r = sd_dhcp_duid_set_llt(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time); + if (r < 0) + return r; + + return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid); } int sd_dhcp_client_set_iaid_duid_ll( @@ -481,23 +497,17 @@ int sd_dhcp_client_set_iaid_duid_ll( bool iaid_set, uint32_t iaid) { - size_t len; + sd_dhcp_duid duid; int r; assert_return(client, -EINVAL); assert_return(!sd_dhcp_client_is_running(client), -EBUSY); - r = dhcp_client_set_iaid(client, iaid_set, iaid); + r = sd_dhcp_duid_set_ll(&duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type); if (r < 0) return r; - r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m"); - - client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len; - - return 0; + return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid); } int sd_dhcp_client_set_iaid_duid_en( @@ -505,23 +515,17 @@ int sd_dhcp_client_set_iaid_duid_en( bool iaid_set, uint32_t iaid) { - size_t len; + sd_dhcp_duid duid; int r; assert_return(client, -EINVAL); assert_return(!sd_dhcp_client_is_running(client), -EBUSY); - r = dhcp_client_set_iaid(client, iaid_set, iaid); + r = sd_dhcp_duid_set_en(&duid); if (r < 0) return r; - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m"); - - client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len; - - return 0; + return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid); } int sd_dhcp_client_set_iaid_duid_uuid( @@ -529,23 +533,17 @@ int sd_dhcp_client_set_iaid_duid_uuid( bool iaid_set, uint32_t iaid) { - size_t len; + sd_dhcp_duid duid; int r; assert_return(client, -EINVAL); assert_return(!sd_dhcp_client_is_running(client), -EBUSY); - r = dhcp_client_set_iaid(client, iaid_set, iaid); + r = sd_dhcp_duid_set_uuid(&duid); if (r < 0) return r; - r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m"); - - client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len; - - return 0; + return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid); } int sd_dhcp_client_set_iaid_duid_raw( @@ -553,27 +551,21 @@ int sd_dhcp_client_set_iaid_duid_raw( bool iaid_set, uint32_t iaid, uint16_t duid_type, - const uint8_t *duid, - size_t duid_len) { + const uint8_t *duid_data, + size_t duid_data_len) { - size_t len; + sd_dhcp_duid duid; int r; assert_return(client, -EINVAL); assert_return(!sd_dhcp_client_is_running(client), -EBUSY); - assert_return(duid || duid_len == 0, -EINVAL); + assert_return(duid_data || duid_data_len == 0, -EINVAL); - r = dhcp_client_set_iaid(client, iaid_set, iaid); + r = sd_dhcp_duid_set(&duid, duid_type, duid_data, duid_data_len); if (r < 0) return r; - r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID: %m"); - - client->client_id_len = sizeof(client->client_id.type) + sizeof(client->client_id.ns.iaid) + len; - - return 0; + return dhcp_client_set_iaid_duid(client, iaid_set, iaid, &duid); } int sd_dhcp_client_set_rapid_commit(sd_dhcp_client *client, bool rapid_commit) { diff --git a/src/libsystemd-network/sd-dhcp-duid.c b/src/libsystemd-network/sd-dhcp-duid.c new file mode 100644 index 0000000000..303e332320 --- /dev/null +++ b/src/libsystemd-network/sd-dhcp-duid.c @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <linux/if_infiniband.h> +#include <net/ethernet.h> +#include <net/if_arp.h> + +#include "dhcp-duid-internal.h" +#include "netif-util.h" +#include "network-common.h" +#include "siphash24.h" +#include "string-table.h" +#include "unaligned.h" + +#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 */ + +static const char * const duid_type_table[_DUID_TYPE_MAX] = { + [DUID_TYPE_LLT] = "DUID-LLT", + [DUID_TYPE_EN] = "DUID-EN/Vendor", + [DUID_TYPE_LL] = "DUID-LL", + [DUID_TYPE_UUID] = "UUID", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); + +int sd_dhcp_duid_clear(sd_dhcp_duid *duid) { + assert_return(duid, -EINVAL); + + *duid = (sd_dhcp_duid) {}; + return 0; +} + +int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid) { + if (!duid) + return false; + + return duid_size_is_valid(duid->size); +} + +int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size) { + assert_return(sd_dhcp_duid_is_set(duid), -EINVAL); + assert_return(ret_type, -EINVAL); + assert_return(ret_data, -EINVAL); + assert_return(ret_size, -EINVAL); + + *ret_type = be16toh(duid->duid.type); + *ret_data = duid->duid.data; + *ret_size = duid->size - offsetof(struct duid, data); + return 0; +} + +int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size) { + assert_return(sd_dhcp_duid_is_set(duid), -EINVAL); + assert_return(ret_data, -EINVAL); + assert_return(ret_size, -EINVAL); + + /* Unlike sd_dhcp_duid_get(), this returns whole DUID including its type. */ + + *ret_data = duid->raw; + *ret_size = duid->size; + return 0; +} + +int sd_dhcp_duid_set( + sd_dhcp_duid *duid, + uint16_t duid_type, + const void *data, + size_t data_size) { + + assert_return(duid, -EINVAL); + assert_return(data, -EINVAL); + assert_return(duid_data_size_is_valid(data_size), -EINVAL); + + unaligned_write_be16(&duid->duid.type, duid_type); + memcpy(duid->duid.data, data, data_size); + + duid->size = offsetof(struct duid, data) + data_size; + return 0; +} + +int sd_dhcp_duid_set_raw( + sd_dhcp_duid *duid, + const void *data, + size_t data_size) { + + assert_return(duid, -EINVAL); + assert_return(data, -EINVAL); + assert_return(duid_size_is_valid(data_size), -EINVAL); + + /* Unlike sd_dhcp_duid_set(), this takes whole DUID including its type. */ + + memcpy(duid->raw, data, data_size); + + duid->size = data_size; + return 0; +} + +int sd_dhcp_duid_set_llt( + sd_dhcp_duid *duid, + const void *hw_addr, + size_t hw_addr_size, + uint16_t arp_type, + uint64_t usec) { + + uint16_t time_from_2000y; + + assert_return(duid, -EINVAL); + assert_return(hw_addr, -EINVAL); + + if (arp_type == ARPHRD_ETHER) + assert_return(hw_addr_size == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL); + else + return -EOPNOTSUPP; + + time_from_2000y = (uint16_t) ((usec_sub_unsigned(usec, USEC_2000) / USEC_PER_SEC) & 0xffffffff); + + unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LLT); + unaligned_write_be16(&duid->duid.llt.htype, arp_type); + unaligned_write_be32(&duid->duid.llt.time, time_from_2000y); + memcpy(duid->duid.llt.haddr, hw_addr, hw_addr_size); + + duid->size = offsetof(struct duid, llt.haddr) + hw_addr_size; + return 0; +} + +int sd_dhcp_duid_set_ll( + sd_dhcp_duid *duid, + const void *hw_addr, + size_t hw_addr_size, + uint16_t arp_type) { + + assert_return(duid, -EINVAL); + assert_return(hw_addr, -EINVAL); + + if (arp_type == ARPHRD_ETHER) + assert_return(hw_addr_size == ETH_ALEN, -EINVAL); + else if (arp_type == ARPHRD_INFINIBAND) + assert_return(hw_addr_size == INFINIBAND_ALEN, -EINVAL); + else + return -EOPNOTSUPP; + + unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_LL); + unaligned_write_be16(&duid->duid.ll.htype, arp_type); + memcpy(duid->duid.ll.haddr, hw_addr, hw_addr_size); + + duid->size = offsetof(struct duid, ll.haddr) + hw_addr_size; + return 0; +} + +int sd_dhcp_duid_set_en(sd_dhcp_duid *duid) { + sd_id128_t machine_id; + bool test_mode; + uint64_t hash; + int r; + + assert_return(duid, -EINVAL); + + test_mode = network_test_mode_enabled(); + + if (!test_mode) { + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + } else + /* For tests, especially for fuzzers, reproducibility is important. + * Hence, use a static and constant machine ID. + * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */ + machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); + + unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_EN); + unaligned_write_be32(&duid->duid.en.pen, SYSTEMD_PEN); + + /* 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 */ + hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); + memcpy(duid->duid.en.id, &hash, sizeof(hash)); + + duid->size = offsetof(struct duid, en.id) + sizeof(hash); + + if (test_mode) + assert_se(memcmp(&duid->duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, duid->size) == 0); + + return 0; +} + +int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid) { + sd_id128_t machine_id; + int r; + + assert_return(duid, -EINVAL); + + r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); + if (r < 0) + return r; + + unaligned_write_be16(&duid->duid.type, SD_DUID_TYPE_UUID); + memcpy(&duid->duid.uuid.uuid, &machine_id, sizeof(machine_id)); + + duid->size = offsetof(struct duid, uuid.uuid) + sizeof(machine_id); + return 0; +} + +int dhcp_identifier_set_iaid( + sd_device *dev, + const struct hw_addr_data *hw_addr, + bool legacy_unstable_byteorder, + void *ret) { + + const char *name = NULL; + uint32_t id32; + uint64_t id; + + assert(hw_addr); + assert(ret); + + if (dev) + name = net_get_persistent_name(dev); + if (name) + id = siphash24(name, strlen(name), HASH_KEY.bytes); + else + /* fall back to MAC address if no predictable name available */ + id = siphash24(hw_addr->bytes, hw_addr->length, HASH_KEY.bytes); + + id32 = (id & 0xffffffff) ^ (id >> 32); + + if (legacy_unstable_byteorder) + /* for historical reasons (a bug), the bits were swapped and thus + * the result was endianness dependent. Preserve that behavior. */ + id32 = bswap_32(id32); + else + /* the fixed behavior returns a stable byte order. Since LE is expected + * to be more common, swap the bytes on LE to give the same as legacy + * behavior. */ + id32 = be32toh(id32); + + unaligned_write_ne32(ret, id32); + return 0; +} diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index c20367dfc9..ed173b1e09 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -12,7 +12,7 @@ #include "alloc-util.h" #include "device-util.h" -#include "dhcp-identifier.h" +#include "dhcp-duid-internal.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" #include "dns-domain.h" @@ -191,10 +191,10 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * static int client_ensure_duid(sd_dhcp6_client *client) { assert(client); - if (client->duid_len != 0) + if (sd_dhcp_duid_is_set(&client->duid)) return 0; - return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + return sd_dhcp6_client_set_duid_en(client); } /** @@ -208,7 +208,7 @@ int sd_dhcp6_client_set_duid_llt(sd_dhcp6_client *client, uint64_t llt_time) { assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - r = dhcp_identifier_set_duid_llt(&client->hw_addr, client->arp_type, llt_time, &client->duid, &client->duid_len); + r = sd_dhcp_duid_set_llt(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type, llt_time); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m"); @@ -221,7 +221,7 @@ int sd_dhcp6_client_set_duid_ll(sd_dhcp6_client *client) { assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - r = dhcp_identifier_set_duid_ll(&client->hw_addr, client->arp_type, &client->duid, &client->duid_len); + r = sd_dhcp_duid_set_ll(&client->duid, client->hw_addr.bytes, client->hw_addr.length, client->arp_type); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m"); @@ -234,7 +234,7 @@ int sd_dhcp6_client_set_duid_en(sd_dhcp6_client *client) { assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + r = sd_dhcp_duid_set_en(&client->duid); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m"); @@ -247,7 +247,7 @@ int sd_dhcp6_client_set_duid_uuid(sd_dhcp6_client *client) { assert_return(client, -EINVAL); assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len); + r = sd_dhcp_duid_set_uuid(&client->duid); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m"); @@ -261,7 +261,7 @@ int sd_dhcp6_client_set_duid_raw(sd_dhcp6_client *client, uint16_t duid_type, co assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(duid || duid_len == 0, -EINVAL); - r = dhcp_identifier_set_duid_raw(duid_type, duid, duid_len, &client->duid, &client->duid_len); + r = sd_dhcp_duid_set(&client->duid, duid_type, duid, duid_len); if (r < 0) return log_dhcp6_client_errno(client, r, "Failed to set DUID: %m"); @@ -276,21 +276,21 @@ int sd_dhcp6_client_duid_as_string( int r; assert_return(client, -EINVAL); - assert_return(client->duid_len > offsetof(struct duid, raw.data), -ENODATA); + assert_return(sd_dhcp_duid_is_set(&client->duid), -ENODATA); assert_return(duid, -EINVAL); - v = duid_type_to_string(be16toh(client->duid.type)); + v = duid_type_to_string(be16toh(client->duid.duid.type)); if (v) { s = strdup(v); if (!s) return -ENOMEM; } else { - r = asprintf(&s, "%0x", client->duid.type); + r = asprintf(&s, "%0x", client->duid.duid.type); if (r < 0) return -ENOMEM; } - t = hexmem(client->duid.raw.data, client->duid_len - offsetof(struct duid, raw.data)); + t = hexmem(client->duid.duid.data, client->duid.size - offsetof(struct duid, data)); if (!t) return -ENOMEM; @@ -825,9 +825,9 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) { if (r < 0) return r; - assert(client->duid_len > 0); + assert(sd_dhcp_duid_is_set(&client->duid)); r = dhcp6_option_append(&buf, &offset, SD_DHCP6_OPTION_CLIENTID, - client->duid_len, &client->duid); + client->duid.size, &client->duid.duid); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index a7180ea585..e5d6547588 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -867,7 +867,7 @@ static int dhcp6_lease_parse_message( "%s message does not contain client ID. Ignoring.", dhcp6_message_type_to_string(message->type)); - if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0) + if (memcmp_nn(clientid, clientid_len, &client->duid.duid, client->duid.size) != 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.", dhcp6_message_type_to_string(message->type)); diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index e3f148daf5..ff3ff2fc4b 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -17,7 +17,7 @@ #include "sd-event.h" #include "alloc-util.h" -#include "dhcp-identifier.h" +#include "dhcp-duid-internal.h" #include "dhcp-network.h" #include "dhcp-option.h" #include "dhcp-packet.h" @@ -165,19 +165,18 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us switch (code) { case SD_DHCP_OPTION_CLIENT_IDENTIFIER: { + sd_dhcp_duid duid; uint32_t iaid; - struct duid duid; - size_t duid_len; - assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0); + assert_se(sd_dhcp_duid_set_en(&duid) >= 0); assert_se(dhcp_identifier_set_iaid(NULL, &hw_addr, /* legacy = */ true, &iaid) >= 0); - assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len); + assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid.size); assert_se(len == 19); assert_se(((uint8_t*) option)[0] == 0xff); assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0); - assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0); + assert_se(memcmp((uint8_t*) option + 5, &duid.duid, duid.size) == 0); break; } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index c9539050b9..ecf3f095c3 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -13,7 +13,7 @@ #include "sd-dhcp6-client.h" #include "sd-event.h" -#include "dhcp-identifier.h" +#include "dhcp-duid-internal.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 080b15387c..ac8baeb94c 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -5,7 +5,6 @@ #include "bus-error.h" #include "bus-locator.h" -#include "dhcp-identifier.h" #include "dhcp-option.h" #include "dhcp6-internal.h" #include "escape.h" diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h index 6e3f3b2a1e..f888e03b96 100644 --- a/src/network/networkd-dhcp-common.h +++ b/src/network/networkd-dhcp-common.h @@ -4,7 +4,7 @@ #include <netinet/in.h> #include "conf-parser.h" -#include "dhcp-identifier.h" +#include "dhcp-duid-internal.h" #include "in-addr-util.h" #include "set.h" #include "time-util.h" diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 47433ef1ab..ac8841681d 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -17,7 +17,6 @@ #include "bus-util.h" #include "device-private.h" #include "device-util.h" -#include "dhcp-identifier.h" #include "dhcp-lease-internal.h" #include "env-file.h" #include "ethtool-util.h" diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 2554e0e031..3f3569f44d 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -8,7 +8,7 @@ #include "sd-netlink.h" #include "sd-resolve.h" -#include "dhcp-identifier.h" +#include "dhcp-duid-internal.h" #include "firewall-util.h" #include "hashmap.h" #include "networkd-link.h" diff --git a/src/systemd/meson.build b/src/systemd/meson.build index a9cdcd24a0..76531dd12a 100644 --- a/src/systemd/meson.build +++ b/src/systemd/meson.build @@ -21,6 +21,7 @@ systemd_headers = files(_systemd_headers) _not_installed_headers = [ 'sd-dhcp-client.h', + 'sd-dhcp-duid.h', 'sd-dhcp-lease.h', 'sd-dhcp-option.h', 'sd-dhcp-protocol.h', diff --git a/src/systemd/sd-dhcp-duid.h b/src/systemd/sd-dhcp-duid.h new file mode 100644 index 0000000000..06e3f8af17 --- /dev/null +++ b/src/systemd/sd-dhcp-duid.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#ifndef foosddhcpduidhfoo +#define foosddhcpduidhfoo + +/*** + Copyright © 2013 Intel Corporation. All rights reserved. + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <https://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <sys/types.h> + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +enum { + SD_DUID_TYPE_LLT = 1, + SD_DUID_TYPE_EN = 2, + SD_DUID_TYPE_LL = 3, + SD_DUID_TYPE_UUID = 4 +}; + +typedef struct sd_dhcp_duid sd_dhcp_duid; + +int sd_dhcp_duid_clear(sd_dhcp_duid *duid); + +int sd_dhcp_duid_is_set(const sd_dhcp_duid *duid); + +int sd_dhcp_duid_get(const sd_dhcp_duid *duid, uint16_t *ret_type, const void **ret_data, size_t *ret_size); +int sd_dhcp_duid_get_raw(const sd_dhcp_duid *duid, const void **ret_data, size_t *ret_size); + +int sd_dhcp_duid_set( + sd_dhcp_duid *duid, + uint16_t duid_type, + const void *data, + size_t data_size); +int sd_dhcp_duid_set_raw( + sd_dhcp_duid *duid, + const void *data, + size_t data_size); +int sd_dhcp_duid_set_llt( + sd_dhcp_duid *duid, + const void *hw_addr, + size_t hw_addr_size, + uint16_t arp_type, + uint64_t usec); +int sd_dhcp_duid_set_ll( + sd_dhcp_duid *duid, + const void *hw_addr, + size_t hw_addr_size, + uint16_t arp_type); +int sd_dhcp_duid_set_en(sd_dhcp_duid *duid); +int sd_dhcp_duid_set_uuid(sd_dhcp_duid *duid); + +_SD_END_DECLARATIONS; + +#endif |