1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/if_infiniband.h>
4 #include <net/ethernet.h>
5 #include <net/if_arp.h>
7 #include "dhcp-identifier.h"
8 #include "netif-util.h"
10 #include "sparse-endian.h"
11 #include "string-table.h"
13 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
14 #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
15 #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
17 static const char * const duid_type_table
[_DUID_TYPE_MAX
] = {
18 [DUID_TYPE_LLT
] = "DUID-LLT",
19 [DUID_TYPE_EN
] = "DUID-EN/Vendor",
20 [DUID_TYPE_LL
] = "DUID-LL",
21 [DUID_TYPE_UUID
] = "UUID",
24 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type
, DUIDType
);
26 int dhcp_identifier_set_duid_llt(
27 const struct hw_addr_data
*hw_addr
,
30 struct duid
*ret_duid
,
33 uint16_t time_from_2000y
;
39 if (hw_addr
->length
== 0)
42 if (arp_type
== ARPHRD_ETHER
)
43 assert_return(hw_addr
->length
== ETH_ALEN
, -EINVAL
);
44 else if (arp_type
== ARPHRD_INFINIBAND
)
45 assert_return(hw_addr
->length
== INFINIBAND_ALEN
, -EINVAL
);
52 time_from_2000y
= (uint16_t) (((t
- USEC_2000
) / USEC_PER_SEC
) & 0xffffffff);
54 unaligned_write_be16(&ret_duid
->type
, DUID_TYPE_LLT
);
55 unaligned_write_be16(&ret_duid
->llt
.htype
, arp_type
);
56 unaligned_write_be32(&ret_duid
->llt
.time
, time_from_2000y
);
57 memcpy(ret_duid
->llt
.haddr
, hw_addr
->bytes
, hw_addr
->length
);
59 *ret_len
= offsetof(struct duid
, llt
.haddr
) + hw_addr
->length
;
64 int dhcp_identifier_set_duid_ll(
65 const struct hw_addr_data
*hw_addr
,
67 struct duid
*ret_duid
,
74 if (hw_addr
->length
== 0)
77 if (arp_type
== ARPHRD_ETHER
)
78 assert_return(hw_addr
->length
== ETH_ALEN
, -EINVAL
);
79 else if (arp_type
== ARPHRD_INFINIBAND
)
80 assert_return(hw_addr
->length
== INFINIBAND_ALEN
, -EINVAL
);
84 unaligned_write_be16(&ret_duid
->type
, DUID_TYPE_LL
);
85 unaligned_write_be16(&ret_duid
->ll
.htype
, arp_type
);
86 memcpy(ret_duid
->ll
.haddr
, hw_addr
->bytes
, hw_addr
->length
);
88 *ret_len
= offsetof(struct duid
, ll
.haddr
) + hw_addr
->length
;
93 int dhcp_identifier_set_duid_en(bool test_mode
, struct duid
*ret_duid
, size_t *ret_len
) {
94 sd_id128_t machine_id
;
102 r
= sd_id128_get_machine(&machine_id
);
106 /* For tests, especially for fuzzers, reproducibility is important.
107 * Hence, use a static and constant machine ID.
108 * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */
109 machine_id
= SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a
, 0b
, 0c
, 0d
, 0e
, 0f
, 10);
111 unaligned_write_be16(&ret_duid
->type
, DUID_TYPE_EN
);
112 unaligned_write_be32(&ret_duid
->en
.pen
, SYSTEMD_PEN
);
114 /* a bit of snake-oil perhaps, but no need to expose the machine-id
115 * directly; duid->en.id might not be aligned, so we need to copy */
116 hash
= htole64(siphash24(&machine_id
, sizeof(machine_id
), HASH_KEY
.bytes
));
117 memcpy(ret_duid
->en
.id
, &hash
, sizeof(hash
));
119 *ret_len
= offsetof(struct duid
, en
.id
) + sizeof(hash
);
122 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);
127 int dhcp_identifier_set_duid_uuid(struct duid
*ret_duid
, size_t *ret_len
) {
128 sd_id128_t machine_id
;
134 r
= sd_id128_get_machine_app_specific(APPLICATION_ID
, &machine_id
);
138 unaligned_write_be16(&ret_duid
->type
, DUID_TYPE_UUID
);
139 memcpy(&ret_duid
->uuid
.uuid
, &machine_id
, sizeof(machine_id
));
141 *ret_len
= offsetof(struct duid
, uuid
.uuid
) + sizeof(machine_id
);
146 int dhcp_identifier_set_duid_raw(
150 struct duid
*ret_duid
,
153 assert(buf
|| buf_len
== 0);
157 if (duid_type
< 0 || duid_type
> UINT16_MAX
)
160 if (buf_len
> MAX_DUID_DATA_LEN
)
163 unaligned_write_be16(&ret_duid
->type
, duid_type
);
164 memcpy_safe(ret_duid
->raw
.data
, buf
, buf_len
);
166 *ret_len
= offsetof(struct duid
, raw
.data
) + buf_len
;
170 int dhcp_identifier_set_iaid(
172 const struct hw_addr_data
*hw_addr
,
173 bool legacy_unstable_byteorder
,
176 const char *name
= NULL
;
184 name
= net_get_persistent_name(dev
);
186 id
= siphash24(name
, strlen(name
), HASH_KEY
.bytes
);
188 /* fall back to MAC address if no predictable name available */
189 id
= siphash24(hw_addr
->bytes
, hw_addr
->length
, HASH_KEY
.bytes
);
191 id32
= (id
& 0xffffffff) ^ (id
>> 32);
193 if (legacy_unstable_byteorder
)
194 /* for historical reasons (a bug), the bits were swapped and thus
195 * the result was endianness dependent. Preserve that behavior. */
196 id32
= bswap_32(id32
);
198 /* the fixed behavior returns a stable byte order. Since LE is expected
199 * to be more common, swap the bytes on LE to give the same as legacy
201 id32
= be32toh(id32
);
203 unaligned_write_ne32(ret
, id32
);