Branch data 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 : 20 : 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 [ - + ]: 20 : assert(duid);
116 [ - + ]: 20 : assert(len);
117 : :
118 : 20 : r = sd_id128_get_machine(&machine_id);
119 [ - + ]: 20 : 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 : 20 : unaligned_write_be16(&duid->type, DUID_TYPE_EN);
128 : 20 : unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
129 : :
130 : 20 : *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 : 20 : hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes));
135 : 20 : memcpy(duid->en.id, &hash, sizeof(duid->en.id));
136 : :
137 : 20 : 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 : 28 : 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 : 28 : _cleanup_(sd_device_unrefp) sd_device *device = NULL;
168 : 28 : const char *name = NULL;
169 : : uint64_t id;
170 : : uint32_t id32;
171 : :
172 [ + - ]: 28 : 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 [ - + ]: 28 : xsprintf(ifindex_str, "n%d", ifindex);
178 [ - + ]: 28 : 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 [ - + ]: 28 : 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 : 28 : id = siphash24(mac, mac_len, HASH_KEY.bytes);
202 : :
203 : 28 : id32 = (id & 0xffffffff) ^ (id >> 32);
204 : :
205 [ + + ]: 28 : 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 : 24 : 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 : 4 : id32 = be32toh(id32);
214 : :
215 : 28 : unaligned_write_ne32(_id, id32);
216 : 28 : return 0;
217 : : }
|