]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp: introduce sd_dhcp_duid and move relevant functions to sd_dhcp_duid.[ch]
authorYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 13 Nov 2023 00:46:16 +0000 (09:46 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Fri, 22 Dec 2023 19:15:09 +0000 (04:15 +0900)
16 files changed:
src/libsystemd-network/dhcp-duid-internal.h [moved from src/libsystemd-network/dhcp-identifier.h with 60% similarity]
src/libsystemd-network/dhcp-identifier.c [deleted file]
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/meson.build
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-duid.c [new file with mode: 0644]
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp6-client.c
src/network/networkd-dhcp-common.c
src/network/networkd-dhcp-common.h
src/network/networkd-link.c
src/network/networkd-manager.h
src/systemd/meson.build
src/systemd/sd-dhcp-duid.h [new file with mode: 0644]

similarity index 60%
rename from src/libsystemd-network/dhcp-identifier.h
rename to src/libsystemd-network/dhcp-duid-internal.h
index cc1a2008a0df492e6088e917e650ea16be5cef5a..ae888f789aa37697eb1fcf5544248525927d77a1 100644 (file)
@@ -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 (file)
index 09bab59..0000000
+++ /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;
-}
index e5b3b1302c1f1ff715ee627ceae4c000e2376704..3fbfc028e99e70ab83dc44cd1e081439d33d1fb6 100644 (file)
@@ -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;
index 93186e23a1abffdc38a74f4366cae56c1916d86b..510b0ed99e6d9a7d5bc1bc49b20e95150e31c156 100644 (file)
@@ -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',
index 67911a2b93aed8a400a8fb4f0a32f3d542830ae1..8e370f1d7ec22c0ac8fe95cf327cb64e70fc42e5 100644 (file)
@@ -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 (file)
index 0000000..303e332
--- /dev/null
@@ -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;
+}
index c20367dfc95baa9f3a02e0622e211d8be6776d1b..ed173b1e09e6eb2a8f6936b619fe6bfec8d5b3b9 100644 (file)
@@ -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;
 
index a7180ea585dda174c75a8e5de53259cc73b4059d..e5d65475881628794ae2d69f9059abfb9add8923 100644 (file)
@@ -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));
index e3f148daf5d3fd23b1a5b6526ca0ef1b42e4e130..ff3ff2fc4bc785fddc0497f71249a5e4c5a0d652 100644 (file)
@@ -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;
         }
 
index c9539050b9a9b29f8bbf0632e937fe16fa0404e6..ecf3f095c39ec268fcfd98c26766cdebcf86c1a3 100644 (file)
@@ -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"
index 080b15387c3a128e7d993496db98470b53e9414b..ac8baeb94ca063c8c25a5057a860cc1abf421bd0 100644 (file)
@@ -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"
index 6e3f3b2a1ef25eed4b7d839c3d9f64b8f31fb6a3..f888e03b961876d52894139ff0915ef69cb58984 100644 (file)
@@ -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"
index 47433ef1ab0b59fe0fc56df8a66fdfc8597054c9..ac8841681d05dfe56ed548b549b9fb4e3412e36e 100644 (file)
@@ -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"
index 2554e0e0310f888ee23a49250d577ee96a6f30b4..3f3569f44d5a2e27f5824bc52ae8ba04ae47526d 100644 (file)
@@ -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"
index a9cdcd24a0a175ac0618f4eae5ccc98f88fd06f4..76531dd12a3c950033b136e2f5c6310f8afa0b08 100644 (file)
@@ -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 (file)
index 0000000..06e3f8a
--- /dev/null
@@ -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