1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include <linux/if_infiniband.h>
4 #include <net/if_arp.h>
9 #include "dhcp-identifier.h"
10 #include "dhcp6-protocol.h"
11 #include "network-internal.h"
12 #include "siphash24.h"
13 #include "sparse-endian.h"
16 #define SYSTEMD_PEN 43793
17 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
18 #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
19 #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
21 int dhcp_validate_duid_len(uint16_t duid_type
, size_t duid_len
, bool strict
) {
24 assert_cc(sizeof(d
.raw
) >= MAX_DUID_LEN
);
25 if (duid_len
> MAX_DUID_LEN
)
29 /* Strict validation is not requested. We only ensure that the
30 * DUID is not too long. */
36 if (duid_len
<= sizeof(d
.llt
))
40 if (duid_len
!= sizeof(d
.en
))
44 if (duid_len
<= sizeof(d
.ll
))
48 if (duid_len
!= sizeof(d
.uuid
))
52 /* accept unknown type in order to be forward compatible */
58 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
) {
59 uint16_t time_from_2000y
;
65 if (arp_type
== ARPHRD_ETHER
)
66 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
67 else if (arp_type
== ARPHRD_INFINIBAND
)
68 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
75 time_from_2000y
= (uint16_t) (((t
- USEC_2000
) / USEC_PER_SEC
) & 0xffffffff);
77 unaligned_write_be16(&duid
->type
, DUID_TYPE_LLT
);
78 unaligned_write_be16(&duid
->llt
.htype
, arp_type
);
79 unaligned_write_be32(&duid
->llt
.time
, time_from_2000y
);
80 memcpy(duid
->llt
.haddr
, addr
, addr_len
);
82 *len
= sizeof(duid
->type
) + sizeof(duid
->llt
.htype
) + sizeof(duid
->llt
.time
) + addr_len
;
87 int dhcp_identifier_set_duid_ll(struct duid
*duid
, const uint8_t *addr
, size_t addr_len
, uint16_t arp_type
, size_t *len
) {
92 if (arp_type
== ARPHRD_ETHER
)
93 assert_return(addr_len
== ETH_ALEN
, -EINVAL
);
94 else if (arp_type
== ARPHRD_INFINIBAND
)
95 assert_return(addr_len
== INFINIBAND_ALEN
, -EINVAL
);
99 unaligned_write_be16(&duid
->type
, DUID_TYPE_LL
);
100 unaligned_write_be16(&duid
->ll
.htype
, arp_type
);
101 memcpy(duid
->ll
.haddr
, addr
, addr_len
);
103 *len
= sizeof(duid
->type
) + sizeof(duid
->ll
.htype
) + addr_len
;
108 int dhcp_identifier_set_duid_en(struct duid
*duid
, size_t *len
) {
109 sd_id128_t machine_id
;
116 r
= sd_id128_get_machine(&machine_id
);
118 #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
119 machine_id
= SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a
, 0b
, 0c
, 0d
, 0e
, 0f
, 10);
125 unaligned_write_be16(&duid
->type
, DUID_TYPE_EN
);
126 unaligned_write_be32(&duid
->en
.pen
, SYSTEMD_PEN
);
128 *len
= sizeof(duid
->type
) + sizeof(duid
->en
);
130 /* a bit of snake-oil perhaps, but no need to expose the machine-id
131 * directly; duid->en.id might not be aligned, so we need to copy */
132 hash
= htole64(siphash24(&machine_id
, sizeof(machine_id
), HASH_KEY
.bytes
));
133 memcpy(duid
->en
.id
, &hash
, sizeof(duid
->en
.id
));
138 int dhcp_identifier_set_duid_uuid(struct duid
*duid
, size_t *len
) {
139 sd_id128_t machine_id
;
145 r
= sd_id128_get_machine_app_specific(APPLICATION_ID
, &machine_id
);
149 unaligned_write_be16(&duid
->type
, DUID_TYPE_UUID
);
150 memcpy(&duid
->raw
.data
, &machine_id
, sizeof(machine_id
));
152 *len
= sizeof(duid
->type
) + sizeof(machine_id
);
157 int dhcp_identifier_set_iaid(
161 bool legacy_unstable_byteorder
,
163 /* name is a pointer to memory in the sd_device struct, so must
164 * have the same scope */
165 _cleanup_(sd_device_unrefp
) sd_device
*device
= NULL
;
166 const char *name
= NULL
;
170 if (detect_container() <= 0) {
171 /* not in a container, udev will be around */
172 char ifindex_str
[2 + DECIMAL_STR_MAX(int)];
175 sprintf(ifindex_str
, "n%d", ifindex
);
176 if (sd_device_new_from_device_id(&device
, ifindex_str
) >= 0) {
177 r
= sd_device_get_is_initialized(device
);
184 name
= net_get_name(device
);
189 id
= siphash24(name
, strlen(name
), HASH_KEY
.bytes
);
191 /* fall back to MAC address if no predictable name available */
192 id
= siphash24(mac
, mac_len
, HASH_KEY
.bytes
);
194 id32
= (id
& 0xffffffff) ^ (id
>> 32);
196 if (legacy_unstable_byteorder
)
197 /* for historical reasons (a bug), the bits were swapped and thus
198 * the result was endianness dependant. Preserve that behavior. */
199 id32
= __bswap_32(id32
);
201 /* the fixed behavior returns a stable byte order. Since LE is expected
202 * to be more common, swap the bytes on LE to give the same as legacy
204 id32
= be32toh(id32
);
206 unaligned_write_ne32(_id
, id32
);