]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/dhcp-identifier.c
Merge pull request #11050 from poettering/resolved-domain-route
[thirdparty/systemd.git] / src / libsystemd-network / dhcp-identifier.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <linux/if_infiniband.h>
4 #include <net/if_arp.h>
5
6 #include "sd-device.h"
7 #include "sd-id128.h"
8
9 #include "dhcp-identifier.h"
10 #include "dhcp6-protocol.h"
11 #include "network-internal.h"
12 #include "siphash24.h"
13 #include "sparse-endian.h"
14 #include "virt.h"
15
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 */
20
21 int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
22 struct duid d;
23
24 assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
25 if (duid_len > MAX_DUID_LEN)
26 return -EINVAL;
27
28 if (!strict) {
29 /* Strict validation is not requested. We only ensure that the
30 * DUID is not too long. */
31 return 0;
32 }
33
34 switch (duid_type) {
35 case DUID_TYPE_LLT:
36 if (duid_len <= sizeof(d.llt))
37 return -EINVAL;
38 break;
39 case DUID_TYPE_EN:
40 if (duid_len != sizeof(d.en))
41 return -EINVAL;
42 break;
43 case DUID_TYPE_LL:
44 if (duid_len <= sizeof(d.ll))
45 return -EINVAL;
46 break;
47 case DUID_TYPE_UUID:
48 if (duid_len != sizeof(d.uuid))
49 return -EINVAL;
50 break;
51 default:
52 /* accept unknown type in order to be forward compatible */
53 break;
54 }
55 return 0;
56 }
57
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;
60
61 assert(duid);
62 assert(len);
63 assert(addr);
64
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);
69 else
70 return -EINVAL;
71
72 if (t < USEC_2000)
73 time_from_2000y = 0;
74 else
75 time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
76
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);
81
82 *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
83
84 return 0;
85 }
86
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) {
88 assert(duid);
89 assert(len);
90 assert(addr);
91
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);
96 else
97 return -EINVAL;
98
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);
102
103 *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
104
105 return 0;
106 }
107
108 int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
109 sd_id128_t machine_id;
110 uint64_t hash;
111 int r;
112
113 assert(duid);
114 assert(len);
115
116 r = sd_id128_get_machine(&machine_id);
117 if (r < 0) {
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);
120 #else
121 return r;
122 #endif
123 }
124
125 unaligned_write_be16(&duid->type, DUID_TYPE_EN);
126 unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
127
128 *len = sizeof(duid->type) + sizeof(duid->en);
129
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));
134
135 return 0;
136 }
137
138 int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
139 sd_id128_t machine_id;
140 int r;
141
142 assert(duid);
143 assert(len);
144
145 r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
146 if (r < 0)
147 return r;
148
149 unaligned_write_be16(&duid->type, DUID_TYPE_UUID);
150 memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
151
152 *len = sizeof(duid->type) + sizeof(machine_id);
153
154 return 0;
155 }
156
157 int dhcp_identifier_set_iaid(
158 int ifindex,
159 const uint8_t *mac,
160 size_t mac_len,
161 bool legacy_unstable_byteorder,
162 void *_id) {
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;
167 uint64_t id;
168 uint32_t id32;
169
170 if (detect_container() <= 0) {
171 /* not in a container, udev will be around */
172 char ifindex_str[2 + DECIMAL_STR_MAX(int)];
173 int r;
174
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);
178 if (r < 0)
179 return r;
180 if (r == 0)
181 /* not yet ready */
182 return -EBUSY;
183
184 name = net_get_name(device);
185 }
186 }
187
188 if (name)
189 id = siphash24(name, strlen(name), HASH_KEY.bytes);
190 else
191 /* fall back to MAC address if no predictable name available */
192 id = siphash24(mac, mac_len, HASH_KEY.bytes);
193
194 id32 = (id & 0xffffffff) ^ (id >> 32);
195
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);
200 else
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
203 * behavior. */
204 id32 = be32toh(id32);
205
206 unaligned_write_ne32(_id, id32);
207 return 0;
208 }