Line data Source code
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 "stdio-util.h"
15 : #include "udev-util.h"
16 : #include "virt.h"
17 :
18 : #define SYSTEMD_PEN 43793
19 : #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
20 : #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
21 : #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
22 :
23 0 : int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) {
24 : struct duid d;
25 :
26 : assert_cc(sizeof(d.raw) >= MAX_DUID_LEN);
27 0 : if (duid_len > MAX_DUID_LEN)
28 0 : return -EINVAL;
29 :
30 0 : if (!strict) {
31 : /* Strict validation is not requested. We only ensure that the
32 : * DUID is not too long. */
33 0 : return 0;
34 : }
35 :
36 0 : switch (duid_type) {
37 0 : case DUID_TYPE_LLT:
38 0 : if (duid_len <= sizeof(d.llt))
39 0 : return -EINVAL;
40 0 : break;
41 0 : case DUID_TYPE_EN:
42 0 : if (duid_len != sizeof(d.en))
43 0 : return -EINVAL;
44 0 : break;
45 0 : case DUID_TYPE_LL:
46 0 : if (duid_len <= sizeof(d.ll))
47 0 : return -EINVAL;
48 0 : break;
49 0 : case DUID_TYPE_UUID:
50 0 : if (duid_len != sizeof(d.uuid))
51 0 : return -EINVAL;
52 0 : break;
53 0 : default:
54 : /* accept unknown type in order to be forward compatible */
55 0 : break;
56 : }
57 0 : return 0;
58 : }
59 :
60 0 : 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) {
61 : uint16_t time_from_2000y;
62 :
63 0 : assert(duid);
64 0 : assert(len);
65 0 : assert(addr);
66 :
67 0 : if (arp_type == ARPHRD_ETHER)
68 0 : assert_return(addr_len == ETH_ALEN, -EINVAL);
69 0 : else if (arp_type == ARPHRD_INFINIBAND)
70 0 : assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
71 : else
72 0 : return -EINVAL;
73 :
74 0 : if (t < USEC_2000)
75 0 : time_from_2000y = 0;
76 : else
77 0 : time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff);
78 :
79 0 : unaligned_write_be16(&duid->type, DUID_TYPE_LLT);
80 0 : unaligned_write_be16(&duid->llt.htype, arp_type);
81 0 : unaligned_write_be32(&duid->llt.time, time_from_2000y);
82 0 : memcpy(duid->llt.haddr, addr, addr_len);
83 :
84 0 : *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len;
85 :
86 0 : return 0;
87 : }
88 :
89 0 : int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) {
90 0 : assert(duid);
91 0 : assert(len);
92 0 : assert(addr);
93 :
94 0 : if (arp_type == ARPHRD_ETHER)
95 0 : assert_return(addr_len == ETH_ALEN, -EINVAL);
96 0 : else if (arp_type == ARPHRD_INFINIBAND)
97 0 : assert_return(addr_len == INFINIBAND_ALEN, -EINVAL);
98 : else
99 0 : return -EINVAL;
100 :
101 0 : unaligned_write_be16(&duid->type, DUID_TYPE_LL);
102 0 : unaligned_write_be16(&duid->ll.htype, arp_type);
103 0 : memcpy(duid->ll.haddr, addr, addr_len);
104 :
105 0 : *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len;
106 :
107 0 : return 0;
108 : }
109 :
110 5 : int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
111 : sd_id128_t machine_id;
112 : uint64_t hash;
113 : int r;
114 :
115 5 : assert(duid);
116 5 : assert(len);
117 :
118 5 : r = sd_id128_get_machine(&machine_id);
119 5 : if (r < 0) {
120 : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
121 : machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10);
122 : #else
123 0 : return r;
124 : #endif
125 : }
126 :
127 5 : unaligned_write_be16(&duid->type, DUID_TYPE_EN);
128 5 : unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
129 :
130 5 : *len = sizeof(duid->type) + sizeof(duid->en);
131 :
132 : /* a bit of snake-oil perhaps, but no need to expose the machine-id
133 : * directly; duid->en.id might not be aligned, so we need to copy */
134 5 : hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
135 5 : memcpy(duid->en.id, &hash, sizeof(duid->en.id));
136 :
137 5 : return 0;
138 : }
139 :
140 0 : int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) {
141 : sd_id128_t machine_id;
142 : int r;
143 :
144 0 : assert(duid);
145 0 : assert(len);
146 :
147 0 : r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id);
148 0 : if (r < 0)
149 0 : return r;
150 :
151 0 : unaligned_write_be16(&duid->type, DUID_TYPE_UUID);
152 0 : memcpy(&duid->raw.data, &machine_id, sizeof(machine_id));
153 :
154 0 : *len = sizeof(duid->type) + sizeof(machine_id);
155 :
156 0 : return 0;
157 : }
158 :
159 7 : int dhcp_identifier_set_iaid(
160 : int ifindex,
161 : const uint8_t *mac,
162 : size_t mac_len,
163 : bool legacy_unstable_byteorder,
164 : void *_id) {
165 : /* name is a pointer to memory in the sd_device struct, so must
166 : * have the same scope */
167 7 : _cleanup_(sd_device_unrefp) sd_device *device = NULL;
168 7 : const char *name = NULL;
169 : uint64_t id;
170 : uint32_t id32;
171 :
172 7 : if (detect_container() <= 0) {
173 : /* not in a container, udev will be around */
174 : char ifindex_str[1 + DECIMAL_STR_MAX(int)];
175 : int r;
176 :
177 7 : xsprintf(ifindex_str, "n%d", ifindex);
178 7 : if (sd_device_new_from_device_id(&device, ifindex_str) >= 0) {
179 0 : r = sd_device_get_is_initialized(device);
180 0 : if (r < 0)
181 0 : return r;
182 0 : if (r == 0)
183 : /* not yet ready */
184 0 : return -EBUSY;
185 :
186 0 : r = device_is_renaming(device);
187 0 : if (r < 0)
188 0 : return r;
189 0 : if (r > 0)
190 : /* device is under renaming */
191 0 : return -EBUSY;
192 :
193 0 : name = net_get_name_persistent(device);
194 : }
195 : }
196 :
197 7 : if (name)
198 0 : id = siphash24(name, strlen(name), HASH_KEY.bytes);
199 : else
200 : /* fall back to MAC address if no predictable name available */
201 7 : id = siphash24(mac, mac_len, HASH_KEY.bytes);
202 :
203 7 : id32 = (id & 0xffffffff) ^ (id >> 32);
204 :
205 7 : if (legacy_unstable_byteorder)
206 : /* for historical reasons (a bug), the bits were swapped and thus
207 : * the result was endianness dependent. Preserve that behavior. */
208 6 : id32 = __bswap_32(id32);
209 : else
210 : /* the fixed behavior returns a stable byte order. Since LE is expected
211 : * to be more common, swap the bytes on LE to give the same as legacy
212 : * behavior. */
213 1 : id32 = be32toh(id32);
214 :
215 7 : unaligned_write_ne32(_id, id32);
216 7 : return 0;
217 : }
|